Split common data model to its own project
This commit is contained in:
Родитель
786a42bdaf
Коммит
4585e03f71
|
@ -16,6 +16,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{8975719B
|
|||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PostgreSql.Exporter", "src\PostgreSql.Exporter\PostgreSql.Exporter.csproj", "{6751E029-5644-4675-812A-4DF61CCD09EE}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Akka.Persistence.Sql.Compat.Common", "src\Akka.Persistence.Sql.Compat.Common\Akka.Persistence.Sql.Compat.Common.csproj", "{835E3257-495E-4C80-BB81-3C5D5F8BC285}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
@ -42,5 +44,9 @@ Global
|
|||
{6751E029-5644-4675-812A-4DF61CCD09EE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{6751E029-5644-4675-812A-4DF61CCD09EE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{6751E029-5644-4675-812A-4DF61CCD09EE}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{835E3257-495E-4C80-BB81-3C5D5F8BC285}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{835E3257-495E-4C80-BB81-3C5D5F8BC285}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{835E3257-495E-4C80-BB81-3C5D5F8BC285}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{835E3257-495E-4C80-BB81-3C5D5F8BC285}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>$(NetCoreLibraryFramework)</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<LangVersion>9.0</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Akka.Cluster.Sharding" Version="$(AkkaVersion)" />
|
||||
<PackageReference Include="Akka.Persistence" Version="$(AkkaVersion)" />
|
||||
<PackageReference Include="Akka.Persistence.Query" Version="$(AkkaVersion)" />
|
||||
<PackageReference Include="Akka.Persistence.Query.Sql" Version="$(AkkaVersion)" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,47 @@
|
|||
// -----------------------------------------------------------------------
|
||||
// <copyright file="CustomSerializer.cs" company="Akka.NET Project">
|
||||
// Copyright (C) 2009-2022 Lightbend Inc. <http://www.lightbend.com>
|
||||
// Copyright (C) 2013-2022 .NET Foundation <https://github.com/akkadotnet/akka.net>
|
||||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using Akka.Actor;
|
||||
using Akka.Serialization;
|
||||
|
||||
namespace Akka.Persistence.Sql.Compat.Common
|
||||
{
|
||||
public sealed class CustomSerializer: SerializerWithStringManifest
|
||||
{
|
||||
public const string CustomShardedMessageManifest = "CSM";
|
||||
|
||||
public CustomSerializer(ExtendedActorSystem system) : base(system)
|
||||
{
|
||||
}
|
||||
|
||||
public override int Identifier => 999;
|
||||
|
||||
public override byte[] ToBinary(object obj)
|
||||
{
|
||||
if (obj is not CustomShardedMessage msg)
|
||||
throw new Exception($"Can only process {nameof(CustomShardedMessage)}");
|
||||
|
||||
return BitConverter.GetBytes(msg.Message);
|
||||
}
|
||||
|
||||
public override object FromBinary(byte[] bytes, string manifest)
|
||||
{
|
||||
if(manifest != CustomShardedMessageManifest)
|
||||
throw new Exception($"Can only process {nameof(CustomShardedMessage)}");
|
||||
|
||||
return new CustomShardedMessage(BitConverter.ToInt32(bytes, 0));
|
||||
}
|
||||
|
||||
public override string Manifest(object obj)
|
||||
{
|
||||
if (obj is not CustomShardedMessage)
|
||||
throw new Exception($"Can only process {nameof(CustomShardedMessage)}");
|
||||
return CustomShardedMessageManifest;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
// -----------------------------------------------------------------------
|
||||
// <copyright file="EventAdapter.cs" company="Akka.NET Project">
|
||||
// Copyright (C) 2013-2022 .NET Foundation <https://github.com/akkadotnet/akka.net>
|
||||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using Akka.Persistence.Journal;
|
||||
|
||||
namespace Akka.Persistence.Sql.Compat.Common
|
||||
{
|
||||
public sealed class EventAdapter: IWriteEventAdapter
|
||||
{
|
||||
public string Manifest(object evt) => string.Empty;
|
||||
|
||||
public object ToJournal(object evt)
|
||||
{
|
||||
var value = evt switch
|
||||
{
|
||||
int i => i,
|
||||
string str => int.Parse(str),
|
||||
_ => throw new Exception($"Unknown type: {evt.GetType()}")
|
||||
};
|
||||
|
||||
return evt.ToTagged(value);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
// -----------------------------------------------------------------------
|
||||
// <copyright file="MessageExtractor.cs" company="Akka.NET Project">
|
||||
// Copyright (C) 2013-2022 .NET Foundation <https://github.com/akkadotnet/akka.net>
|
||||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
using Akka.Cluster.Sharding;
|
||||
|
||||
namespace Akka.Persistence.Sql.Compat.Common
|
||||
{
|
||||
public sealed class MessageExtractor : HashCodeMessageExtractor
|
||||
{
|
||||
/// <summary>
|
||||
/// We only ever run three nodes, so ~10 shards per node
|
||||
/// </summary>
|
||||
public MessageExtractor() : base(30)
|
||||
{
|
||||
}
|
||||
|
||||
public override string? EntityId(object message)
|
||||
=> message switch
|
||||
{
|
||||
int i => i.ToEntityId(),
|
||||
string str => int.Parse(str).ToEntityId(),
|
||||
IHasEntityId msg => msg.EntityId,
|
||||
ShardingEnvelope msg => msg.EntityId,
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
// -----------------------------------------------------------------------
|
||||
// <copyright file="Messages.cs" company="Akka.NET Project">
|
||||
// Copyright (C) 2013-2022 .NET Foundation <https://github.com/akkadotnet/akka.net>
|
||||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
namespace Akka.Persistence.Sql.Compat.Common
|
||||
{
|
||||
public interface IHasEntityId
|
||||
{
|
||||
public string EntityId { get; }
|
||||
}
|
||||
|
||||
public sealed class Finish: IHasEntityId
|
||||
{
|
||||
public Finish(int entityId)
|
||||
{
|
||||
EntityId = (entityId % Utils.MaxEntities).ToString();
|
||||
}
|
||||
|
||||
public string EntityId { get; }
|
||||
}
|
||||
|
||||
public sealed class TakeSnapshotAndClear: IHasEntityId
|
||||
{
|
||||
public TakeSnapshotAndClear(int entityId)
|
||||
{
|
||||
EntityId = (entityId % Utils.MaxEntities).ToString();
|
||||
}
|
||||
|
||||
public string EntityId { get; }
|
||||
}
|
||||
|
||||
public sealed class TakeSnapshot: IHasEntityId
|
||||
{
|
||||
public TakeSnapshot(int entityId)
|
||||
{
|
||||
EntityId = (entityId % Utils.MaxEntities).ToString();
|
||||
}
|
||||
|
||||
public string EntityId { get; }
|
||||
}
|
||||
|
||||
public sealed class ShardedMessage: IHasEntityId
|
||||
{
|
||||
public ShardedMessage(int message)
|
||||
{
|
||||
EntityId = message.ToEntityId();
|
||||
Message = message;
|
||||
}
|
||||
|
||||
public string EntityId { get; }
|
||||
|
||||
public int Message { get; }
|
||||
}
|
||||
|
||||
public sealed class CustomShardedMessage: IHasEntityId
|
||||
{
|
||||
public CustomShardedMessage(int message)
|
||||
{
|
||||
EntityId = message.ToEntityId();
|
||||
Message = message;
|
||||
}
|
||||
|
||||
public string EntityId { get; }
|
||||
|
||||
public int Message { get; }
|
||||
}
|
||||
|
||||
public sealed class StateSnapshot
|
||||
{
|
||||
public static readonly StateSnapshot Empty = new StateSnapshot(0, 0);
|
||||
|
||||
public StateSnapshot(int total, int persisted)
|
||||
{
|
||||
Total = total;
|
||||
Persisted = persisted;
|
||||
}
|
||||
|
||||
public int Total { get; }
|
||||
public int Persisted { get; }
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
// -----------------------------------------------------------------------
|
||||
// <copyright file="Utils.cs" company="Akka.NET Project">
|
||||
// Copyright (C) 2013-2022 .NET Foundation <https://github.com/akkadotnet/akka.net>
|
||||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using Akka.Persistence.Journal;
|
||||
|
||||
namespace Akka.Persistence.Sql.Compat.Common
|
||||
{
|
||||
public static class Utils
|
||||
{
|
||||
// We're only doing 100 entities
|
||||
public const int MaxEntities = 100;
|
||||
private const int TaggedVariants = 3;
|
||||
public const int MessagesPerType = MaxEntities * TaggedVariants;
|
||||
|
||||
public static readonly string[] Tags = { "Tag1", "Tag2", "Tag3", "Tag4" };
|
||||
|
||||
public static object ToTagged<T>(this T msg, int value)
|
||||
{
|
||||
if (msg is null)
|
||||
throw new ArgumentNullException(nameof(msg));
|
||||
|
||||
return (value % TaggedVariants) switch
|
||||
{
|
||||
0 => msg,
|
||||
1 => new Tagged(msg, new[] { Tags[0] }),
|
||||
_ => new Tagged(msg, new[] { Tags[0], Tags[1] }),
|
||||
};
|
||||
}
|
||||
|
||||
public static string ToEntityId(this int msg)
|
||||
=> ((msg / 3) % MaxEntities).ToString();
|
||||
}
|
||||
}
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>$(NetCoreFramework)</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -14,4 +16,8 @@
|
|||
<PackageReference Include="SharpCompress" Version="0.32.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Akka.Persistence.Sql.Compat.Common\Akka.Persistence.Sql.Compat.Common.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -1,45 +0,0 @@
|
|||
// -----------------------------------------------------------------------
|
||||
// <copyright file="CustomSerializer.cs" company="Akka.NET Project">
|
||||
// Copyright (C) 2009-2022 Lightbend Inc. <http://www.lightbend.com>
|
||||
// Copyright (C) 2013-2022 .NET Foundation <https://github.com/akkadotnet/akka.net>
|
||||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
using Akka.Actor;
|
||||
using Akka.Serialization;
|
||||
|
||||
namespace Akka.Persistence.Sql.Exporter.Shared.Test;
|
||||
|
||||
public sealed class CustomSerializer: SerializerWithStringManifest
|
||||
{
|
||||
public const string CustomShardedMessageManifest = "CSM";
|
||||
|
||||
public CustomSerializer(ExtendedActorSystem system) : base(system)
|
||||
{
|
||||
}
|
||||
|
||||
public override int Identifier => 999;
|
||||
|
||||
public override byte[] ToBinary(object obj)
|
||||
{
|
||||
if (obj is not CustomShardedMessage msg)
|
||||
throw new Exception($"Can only process {nameof(CustomShardedMessage)}");
|
||||
|
||||
return BitConverter.GetBytes(msg.Message);
|
||||
}
|
||||
|
||||
public override object FromBinary(byte[] bytes, string manifest)
|
||||
{
|
||||
if(manifest != CustomShardedMessageManifest)
|
||||
throw new Exception($"Can only process {nameof(CustomShardedMessage)}");
|
||||
|
||||
return new CustomShardedMessage(BitConverter.ToInt32(bytes));
|
||||
}
|
||||
|
||||
public override string Manifest(object obj)
|
||||
{
|
||||
if (obj is not CustomShardedMessage)
|
||||
throw new Exception($"Can only process {nameof(CustomShardedMessage)}");
|
||||
return CustomShardedMessageManifest;
|
||||
}
|
||||
}
|
|
@ -4,6 +4,8 @@
|
|||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
using Akka.Persistence.Sql.Compat.Common;
|
||||
|
||||
namespace Akka.Persistence.Sql.Exporter.Shared.Test;
|
||||
|
||||
using Akka.Actor;
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
using Akka.Actor;
|
||||
using Akka.Event;
|
||||
using Akka.Persistence.Journal;
|
||||
using Akka.Persistence.Sql.Compat.Common;
|
||||
|
||||
namespace Akka.Persistence.Sql.Exporter.Shared.Test;
|
||||
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
// -----------------------------------------------------------------------
|
||||
// <copyright file="EventAdapter.cs" company="Akka.NET Project">
|
||||
// Copyright (C) 2013-2022 .NET Foundation <https://github.com/akkadotnet/akka.net>
|
||||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
using Akka.Persistence.Journal;
|
||||
|
||||
namespace Akka.Persistence.Sql.Exporter.Shared.Test;
|
||||
|
||||
public sealed class EventAdapter: IWriteEventAdapter
|
||||
{
|
||||
public string Manifest(object evt) => string.Empty;
|
||||
|
||||
public object ToJournal(object evt)
|
||||
{
|
||||
var value = evt switch
|
||||
{
|
||||
int i => i,
|
||||
string str => int.Parse(str),
|
||||
_ => throw new Exception($"Unknown type: {evt.GetType()}")
|
||||
};
|
||||
|
||||
return evt.ToTagged(value);
|
||||
}
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
// -----------------------------------------------------------------------
|
||||
// <copyright file="MessageExtractor.cs" company="Akka.NET Project">
|
||||
// Copyright (C) 2013-2022 .NET Foundation <https://github.com/akkadotnet/akka.net>
|
||||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
using Akka.Cluster.Sharding;
|
||||
|
||||
namespace Akka.Persistence.Sql.Exporter.Shared.Test;
|
||||
|
||||
public sealed class MessageExtractor : HashCodeMessageExtractor
|
||||
{
|
||||
/// <summary>
|
||||
/// We only ever run three nodes, so ~10 shards per node
|
||||
/// </summary>
|
||||
public MessageExtractor() : base(30)
|
||||
{
|
||||
}
|
||||
|
||||
public override string? EntityId(object message)
|
||||
=> message switch
|
||||
{
|
||||
int i => i.ToEntityId(),
|
||||
string str => int.Parse(str).ToEntityId(),
|
||||
IHasEntityId msg => msg.EntityId,
|
||||
ShardingEnvelope msg => msg.EntityId,
|
||||
_ => null
|
||||
};
|
||||
}
|
|
@ -1,82 +0,0 @@
|
|||
// -----------------------------------------------------------------------
|
||||
// <copyright file="Messages.cs" company="Akka.NET Project">
|
||||
// Copyright (C) 2013-2022 .NET Foundation <https://github.com/akkadotnet/akka.net>
|
||||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
namespace Akka.Persistence.Sql.Exporter.Shared.Test;
|
||||
|
||||
public interface IHasEntityId
|
||||
{
|
||||
public string EntityId { get; }
|
||||
}
|
||||
|
||||
public sealed class Finish: IHasEntityId
|
||||
{
|
||||
public Finish(int entityId)
|
||||
{
|
||||
EntityId = (entityId % Utils.MaxEntities).ToString();
|
||||
}
|
||||
|
||||
public string EntityId { get; }
|
||||
}
|
||||
|
||||
public sealed class TakeSnapshotAndClear: IHasEntityId
|
||||
{
|
||||
public TakeSnapshotAndClear(int entityId)
|
||||
{
|
||||
EntityId = (entityId % Utils.MaxEntities).ToString();
|
||||
}
|
||||
|
||||
public string EntityId { get; }
|
||||
}
|
||||
|
||||
public sealed class TakeSnapshot: IHasEntityId
|
||||
{
|
||||
public TakeSnapshot(int entityId)
|
||||
{
|
||||
EntityId = (entityId % Utils.MaxEntities).ToString();
|
||||
}
|
||||
|
||||
public string EntityId { get; }
|
||||
}
|
||||
|
||||
public sealed class ShardedMessage: IHasEntityId
|
||||
{
|
||||
public ShardedMessage(int message)
|
||||
{
|
||||
EntityId = message.ToEntityId();
|
||||
Message = message;
|
||||
}
|
||||
|
||||
public string EntityId { get; }
|
||||
|
||||
public int Message { get; }
|
||||
}
|
||||
|
||||
public sealed class CustomShardedMessage: IHasEntityId
|
||||
{
|
||||
public CustomShardedMessage(int message)
|
||||
{
|
||||
EntityId = message.ToEntityId();
|
||||
Message = message;
|
||||
}
|
||||
|
||||
public string EntityId { get; }
|
||||
|
||||
public int Message { get; }
|
||||
}
|
||||
|
||||
public sealed class StateSnapshot
|
||||
{
|
||||
public static readonly StateSnapshot Empty = new StateSnapshot(0, 0);
|
||||
|
||||
public StateSnapshot(int total, int persisted)
|
||||
{
|
||||
Total = total;
|
||||
Persisted = persisted;
|
||||
}
|
||||
|
||||
public int Total { get; }
|
||||
public int Persisted { get; }
|
||||
}
|
|
@ -12,6 +12,7 @@ using Akka.Cluster.Hosting;
|
|||
using Akka.Cluster.Sharding;
|
||||
using Akka.Hosting;
|
||||
using Akka.Persistence.Hosting;
|
||||
using Akka.Persistence.Sql.Compat.Common;
|
||||
using Akka.Remote.Hosting;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
// -----------------------------------------------------------------------
|
||||
// <copyright file="Utils.cs" company="Akka.NET Project">
|
||||
// Copyright (C) 2013-2022 .NET Foundation <https://github.com/akkadotnet/akka.net>
|
||||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
using Akka.Persistence.Journal;
|
||||
|
||||
namespace Akka.Persistence.Sql.Exporter.Shared.Test;
|
||||
|
||||
public static class Utils
|
||||
{
|
||||
// We're only doing 100 entities
|
||||
public const int MaxEntities = 100;
|
||||
private const int TaggedVariants = 3;
|
||||
public const int MessagesPerType = MaxEntities * TaggedVariants;
|
||||
|
||||
public static readonly string[] Tags = { "Tag1", "Tag2", "Tag3", "Tag4" };
|
||||
|
||||
public static object ToTagged<T>(this T msg, int value)
|
||||
{
|
||||
if (msg is null)
|
||||
throw new ArgumentNullException(nameof(msg));
|
||||
|
||||
return (value % TaggedVariants) switch
|
||||
{
|
||||
0 => msg,
|
||||
1 => new Tagged(msg, new[] { Tags[0] }),
|
||||
_ => new Tagged(msg, new[] { Tags[0], Tags[1] }),
|
||||
};
|
||||
}
|
||||
|
||||
public static string ToEntityId(this int msg)
|
||||
=> ((msg / 3) % MaxEntities).ToString();
|
||||
}
|
|
@ -213,11 +213,11 @@
|
|||
</License>
|
||||
<NoWarn>$(NoWarn);CS1591</NoWarn>
|
||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<NetCoreLibraryFramework>netstandard2.0</NetCoreLibraryFramework>
|
||||
<NetCoreFramework>net6.0</NetCoreFramework>
|
||||
<AkkaHostingVersion>0.5.0</AkkaHostingVersion>
|
||||
<AkkaVersion>1.4.43</AkkaVersion>
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>$(NetCoreFramework)</TargetFramework>
|
||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>$(NetCoreFramework)</TargetFramework>
|
||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>$(NetCoreFramework)</TargetFramework>
|
||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>$(NetCoreFramework)</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче