diff --git a/src/net/KEFCore/Extensions/KafkaEntityTypeExtensions.cs b/src/net/KEFCore/Extensions/KafkaEntityTypeExtensions.cs
index 3bfce2b9..bb892bff 100644
--- a/src/net/KEFCore/Extensions/KafkaEntityTypeExtensions.cs
+++ b/src/net/KEFCore/Extensions/KafkaEntityTypeExtensions.cs
@@ -117,4 +117,10 @@ public static int NumPartitions(this IEntityType entityType, KafkaOptionsExtensi
var numPartitions = options.DefaultNumPartitions;
return numPartitions;
}
+
+ public static int? ConsumerInstances(this IEntityType entityType, KafkaOptionsExtension options)
+ {
+ var consumerInstances = options.DefaultConsumerInstances;
+ return consumerInstances;
+ }
}
diff --git a/src/net/KEFCore/Infrastructure/Internal/IKafkaSingletonOptions.cs b/src/net/KEFCore/Infrastructure/Internal/IKafkaSingletonOptions.cs
index 68b9d979..33b56157 100644
--- a/src/net/KEFCore/Infrastructure/Internal/IKafkaSingletonOptions.cs
+++ b/src/net/KEFCore/Infrastructure/Internal/IKafkaSingletonOptions.cs
@@ -36,10 +36,14 @@ public interface IKafkaSingletonOptions : ISingletonOptions
bool ProducerByEntity { get; }
+ bool UseCompactedReplicator { get; }
+
bool UsePersistentStorage { get; }
int DefaultNumPartitions { get; }
+ int? DefaultConsumerInstances { get; }
+
int DefaultReplicationFactor { get; }
ProducerConfigBuilder? ProducerConfigBuilder { get; }
diff --git a/src/net/KEFCore/Infrastructure/Internal/KafkaOptionsExtension.cs b/src/net/KEFCore/Infrastructure/Internal/KafkaOptionsExtension.cs
index e08853fd..ca3ece5a 100644
--- a/src/net/KEFCore/Infrastructure/Internal/KafkaOptionsExtension.cs
+++ b/src/net/KEFCore/Infrastructure/Internal/KafkaOptionsExtension.cs
@@ -20,7 +20,6 @@
using Java.Lang;
using Java.Util;
-using MASES.JCOBridge.C2JBridge;
using MASES.KNet.Common;
using MASES.KNet.Producer;
using MASES.KNet.Streams;
@@ -28,7 +27,6 @@
using Org.Apache.Kafka.Clients.Producer;
using Org.Apache.Kafka.Streams;
using System.Globalization;
-using System.Text;
namespace MASES.EntityFrameworkCore.KNet.Infrastructure.Internal;
@@ -39,8 +37,10 @@ public class KafkaOptionsExtension : IDbContextOptionsExtension
private string? _applicationId;
private string? _bootstrapServers;
private bool _producerByEntity = false;
+ private bool _useCompactedReplicator = false;
private bool _usePersistentStorage = false;
private int _defaultNumPartitions = 1;
+ private int? _defaultConsumerInstances = null;
private short _defaultReplicationFactor = 1;
private ProducerConfigBuilder? _producerConfigBuilder;
private StreamsConfigBuilder? _streamsConfigBuilder;
@@ -61,8 +61,10 @@ protected KafkaOptionsExtension(KafkaOptionsExtension copyFrom)
_applicationId = copyFrom._applicationId;
_bootstrapServers = copyFrom._bootstrapServers;
_producerByEntity = copyFrom._producerByEntity;
+ _useCompactedReplicator = copyFrom._useCompactedReplicator;
_usePersistentStorage = copyFrom._usePersistentStorage;
_defaultNumPartitions = copyFrom._defaultNumPartitions;
+ _defaultConsumerInstances = copyFrom._defaultConsumerInstances;
_defaultReplicationFactor = copyFrom._defaultReplicationFactor;
_producerConfigBuilder = ProducerConfigBuilder.CreateFrom(copyFrom._producerConfigBuilder);
_streamsConfigBuilder = StreamsConfigBuilder.CreateFrom(copyFrom._streamsConfigBuilder);
@@ -85,10 +87,14 @@ protected KafkaOptionsExtension(KafkaOptionsExtension copyFrom)
public virtual bool ProducerByEntity => _producerByEntity;
+ public virtual bool UseCompactedReplicator => _useCompactedReplicator;
+
public virtual bool UsePersistentStorage => _usePersistentStorage;
public virtual int DefaultNumPartitions => _defaultNumPartitions;
+ public virtual int? DefaultConsumerInstances => _defaultConsumerInstances;
+
public virtual short DefaultReplicationFactor => _defaultReplicationFactor;
public virtual ProducerConfigBuilder ProducerConfigBuilder => _producerConfigBuilder!;
@@ -142,6 +148,15 @@ public virtual KafkaOptionsExtension WithProducerByEntity(bool producerByEntity
return clone;
}
+ public virtual KafkaOptionsExtension WithCompactedReplicator(bool useCompactedReplicator = false)
+ {
+ var clone = Clone();
+
+ clone._useCompactedReplicator = useCompactedReplicator;
+
+ return clone;
+ }
+
public virtual KafkaOptionsExtension WithUsePersistentStorage(bool usePersistentStorage = false)
{
var clone = Clone();
@@ -160,6 +175,15 @@ public virtual KafkaOptionsExtension WithDefaultNumPartitions(int defaultNumPart
return clone;
}
+ public virtual KafkaOptionsExtension WithDefaultConsumerInstances(int? defaultConsumerInstances = null)
+ {
+ var clone = Clone();
+
+ clone._defaultConsumerInstances = defaultConsumerInstances;
+
+ return clone;
+ }
+
public virtual KafkaOptionsExtension WithDefaultReplicationFactor(short defaultReplicationFactor = 1)
{
var clone = Clone();
diff --git a/src/net/KEFCore/Infrastructure/Internal/KafkaSingletonOptions.cs b/src/net/KEFCore/Infrastructure/Internal/KafkaSingletonOptions.cs
index 585d9367..ced2c8b6 100644
--- a/src/net/KEFCore/Infrastructure/Internal/KafkaSingletonOptions.cs
+++ b/src/net/KEFCore/Infrastructure/Internal/KafkaSingletonOptions.cs
@@ -35,8 +35,10 @@ public virtual void Initialize(IDbContextOptions options)
ApplicationId = kafkaOptions.ApplicationId;
BootstrapServers = kafkaOptions.BootstrapServers;
ProducerByEntity = kafkaOptions.ProducerByEntity;
+ UseCompactedReplicator = kafkaOptions.UseCompactedReplicator;
UsePersistentStorage = kafkaOptions.UsePersistentStorage;
DefaultNumPartitions = kafkaOptions.DefaultNumPartitions;
+ DefaultConsumerInstances = kafkaOptions.DefaultConsumerInstances;
DefaultReplicationFactor = kafkaOptions.DefaultReplicationFactor;
ProducerConfigBuilder = ProducerConfigBuilder.CreateFrom(kafkaOptions.ProducerConfigBuilder);
StreamsConfigBuilder = StreamsConfigBuilder.CreateFrom(kafkaOptions.StreamsConfigBuilder);
@@ -68,10 +70,14 @@ public virtual void Validate(IDbContextOptions options)
public virtual bool ProducerByEntity { get; private set; }
+ public virtual bool UseCompactedReplicator { get; private set; }
+
public virtual bool UsePersistentStorage { get; private set; }
public virtual int DefaultNumPartitions { get; private set; }
+ public virtual int? DefaultConsumerInstances { get; private set; }
+
public virtual int DefaultReplicationFactor { get; private set; }
public virtual ProducerConfigBuilder? ProducerConfigBuilder { get; private set; }
diff --git a/src/net/KEFCore/Infrastructure/KafkaDbContext.cs b/src/net/KEFCore/Infrastructure/KafkaDbContext.cs
index 959ef782..7972863d 100644
--- a/src/net/KEFCore/Infrastructure/KafkaDbContext.cs
+++ b/src/net/KEFCore/Infrastructure/KafkaDbContext.cs
@@ -19,6 +19,7 @@
* Refer to LICENSE for more information.
*/
+using MASES.KNet;
using MASES.KNet.Common;
using MASES.KNet.Producer;
using MASES.KNet.Streams;
@@ -30,41 +31,66 @@ namespace MASES.EntityFrameworkCore.KNet.Infrastructure;
///
public class KafkaDbContext : DbContext
{
+ ///
+ public KafkaDbContext()
+ {
+
+ }
+ ///
+ public KafkaDbContext(DbContextOptions options) : base(options)
+ {
+
+ }
+
///
/// The bootstrap servers of the Apache Kafka cluster
///
- public string? BootstrapServers { get; set; }
+ public virtual string? BootstrapServers { get; set; }
///
/// The application id
///
- public string ApplicationId { get; set; } = Guid.NewGuid().ToString();
+ public virtual string ApplicationId { get; set; } = Guid.NewGuid().ToString();
///
/// Database name
///
- public string? DbName { get; set; }
+ public virtual string? DbName { get; set; }
///
/// Database number of partitions
///
- public int DefaultNumPartitions { get; set; } = 10;
+ public virtual int DefaultNumPartitions { get; set; } = 10;
///
/// Database replication factor
///
- public short DefaultReplicationFactor { get; set; } = 1;
+ public virtual short DefaultReplicationFactor { get; set; } = 1;
+ ///
+ /// Database consumr instances used in conjunction with
+ ///
+ public virtual int? DefaultConsumerInstances { get; set; } = null;
///
/// Use persistent storage
///
- public bool UsePersistentStorage { get; set; } = false;
+ public virtual bool UsePersistentStorage { get; set; } = false;
///
/// Use a producer for each Entity
///
public bool UseProducerByEntity { get; set; } = false;
-
- public ProducerConfigBuilder? ProducerConfigBuilder { get; set; }
-
- public StreamsConfigBuilder? StreamsConfigBuilder { get; set; }
-
- public TopicConfigBuilder? TopicConfigBuilder { get; set; }
-
+ ///
+ /// Use instead of Apache Kafka Streams
+ ///
+ public virtual bool UseCompactedReplicator { get; set; } = false;
+ ///
+ /// The optional
+ ///
+ public virtual ProducerConfigBuilder? ProducerConfigBuilder { get; set; }
+ ///
+ /// The optional
+ ///
+ public virtual StreamsConfigBuilder? StreamsConfigBuilder { get; set; }
+ ///
+ /// The optional
+ ///
+ public virtual TopicConfigBuilder? TopicConfigBuilder { get; set; }
+ ///
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (BootstrapServers == null)
@@ -76,9 +102,10 @@ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
optionsBuilder.UseKafkaDatabase(ApplicationId, DbName, BootstrapServers, (o) =>
{
- o.StreamsConfig(StreamsConfigBuilder??o.EmptyStreamsConfigBuilder).WithDefaultNumPartitions(DefaultNumPartitions);
+ o.StreamsConfig(StreamsConfigBuilder ?? o.EmptyStreamsConfigBuilder).WithDefaultNumPartitions(DefaultNumPartitions);
o.WithUsePersistentStorage(UsePersistentStorage);
o.WithProducerByEntity(UseProducerByEntity);
+ o.WithCompactedReplicator(UseCompactedReplicator);
o.WithDefaultReplicationFactor(DefaultReplicationFactor);
});
}
diff --git a/src/net/KEFCore/Infrastructure/KafkaDbContextOptionsBuilder.cs b/src/net/KEFCore/Infrastructure/KafkaDbContextOptionsBuilder.cs
index e79c6138..7271b5a3 100644
--- a/src/net/KEFCore/Infrastructure/KafkaDbContextOptionsBuilder.cs
+++ b/src/net/KEFCore/Infrastructure/KafkaDbContextOptionsBuilder.cs
@@ -114,6 +114,27 @@ public virtual KafkaDbContextOptionsBuilder WithProducerByEntity(bool producerBy
return this;
}
+ ///
+ /// Enables use of
+ ///
+ ///
+ /// See Using DbContextOptions, and
+ /// The EF Core Kafka database provider for more information and examples.
+ ///
+ /// If then will be used instead of Apache Kafka Streams.
+ /// The same builder instance so that multiple calls can be chained.
+ public virtual KafkaDbContextOptionsBuilder WithCompactedReplicator(bool useCompactedReplicator = false)
+ {
+ var extension = OptionsBuilder.Options.FindExtension()
+ ?? new KafkaOptionsExtension();
+
+ extension = extension.WithCompactedReplicator(useCompactedReplicator);
+
+ ((IDbContextOptionsBuilderInfrastructure)OptionsBuilder).AddOrUpdateExtension(extension);
+
+ return this;
+ }
+
///
/// Enables use of persistent storage, otherwise a storage will be in-memory
///
@@ -156,6 +177,27 @@ public virtual KafkaDbContextOptionsBuilder WithDefaultNumPartitions(int default
return this;
}
+ ///
+ /// Defines the default number of consumer instances to be used in conjunction with
+ ///
+ ///
+ /// See Using DbContextOptions, and
+ /// The EF Core Kafka database provider for more information and examples.
+ ///
+ /// The default number of consumer instances to be used in conjunction with
+ /// The same builder instance so that multiple calls can be chained.
+ public virtual KafkaDbContextOptionsBuilder WithDefaultConsumerInstances(int? defaultConsumerInstances = null)
+ {
+ var extension = OptionsBuilder.Options.FindExtension()
+ ?? new KafkaOptionsExtension();
+
+ extension = extension.WithDefaultConsumerInstances(defaultConsumerInstances);
+
+ ((IDbContextOptionsBuilderInfrastructure)OptionsBuilder).AddOrUpdateExtension(extension);
+
+ return this;
+ }
+
///
/// Defines the default replication factor to use when a new topic is created
///
diff --git a/src/net/KEFCore/KEFCore.csproj b/src/net/KEFCore/KEFCore.csproj
index 52d4f510..ce0f6f01 100644
--- a/src/net/KEFCore/KEFCore.csproj
+++ b/src/net/KEFCore/KEFCore.csproj
@@ -13,7 +13,7 @@
README.md
enable
true
- False
+ True
False
@@ -66,10 +66,10 @@
-
+
All
None
-
+
diff --git a/src/net/KEFCore/Serdes/Internal/IKafkaSerdesEntityType.cs b/src/net/KEFCore/Serdes/Internal/IKafkaSerdesEntityType.cs
index 5fc790bf..21b0c78d 100644
--- a/src/net/KEFCore/Serdes/Internal/IKafkaSerdesEntityType.cs
+++ b/src/net/KEFCore/Serdes/Internal/IKafkaSerdesEntityType.cs
@@ -22,12 +22,20 @@ namespace MASES.EntityFrameworkCore.KNet.Serdes.Internal
{
public interface IKafkaSerdesEntityType
{
+ string Serialize(params object?[]? args);
+
string Serialize(Headers headers, params object?[]? args);
+ string Serialize(TKey key);
+
string Serialize(Headers headers, TKey key);
+ object[] Deserialize(string arg);
+
object[] Deserialize(Headers headers, string arg);
+ TKey Deserialize(string arg);
+
TKey Deserialize(Headers headers, string arg);
object[] ConvertData(object[]? input);
diff --git a/src/net/KEFCore/Serdes/Internal/KafkaSerdesEntityType.cs b/src/net/KEFCore/Serdes/Internal/KafkaSerdesEntityType.cs
index ba5806da..4a666b5c 100644
--- a/src/net/KEFCore/Serdes/Internal/KafkaSerdesEntityType.cs
+++ b/src/net/KEFCore/Serdes/Internal/KafkaSerdesEntityType.cs
@@ -50,16 +50,28 @@ public KafkaSerdesEntityType(IEntityType type)
_properties = _type.GetProperties().ToArray();
}
+ public object[] Deserialize(string arg)
+ {
+ var des = GetFullType(arg);
+ return ConvertData(des!.data);
+ }
+
public object[] Deserialize(Headers headers, string arg)
{
var des = GetFullType(arg);
return ConvertData(des!.data);
}
+ public TKey Deserialize(string arg) => System.Text.Json.JsonSerializer.Deserialize(arg)!;
+
public TKey Deserialize(Headers headers, string arg) => System.Text.Json.JsonSerializer.Deserialize(arg)!;
+ public string Serialize(params object?[]? args) => System.Text.Json.JsonSerializer.Serialize(new KafkaSerdesEntityTypeData(_type.Name, args!));
+
public string Serialize(Headers headers, params object?[]? args) => System.Text.Json.JsonSerializer.Serialize(new KafkaSerdesEntityTypeData(_type.Name, args!));
+ public string Serialize(TKey key) => System.Text.Json.JsonSerializer.Serialize(key);
+
public string Serialize(Headers headers, TKey key) => System.Text.Json.JsonSerializer.Serialize(key);
public static KafkaSerdesEntityTypeData? GetFullType(string arg) => System.Text.Json.JsonSerializer.Deserialize(arg);
diff --git a/src/net/KEFCore/Storage/Internal/IKafkaCluster.cs b/src/net/KEFCore/Storage/Internal/IKafkaCluster.cs
index 5cdaa59a..fe833f8c 100644
--- a/src/net/KEFCore/Storage/Internal/IKafkaCluster.cs
+++ b/src/net/KEFCore/Storage/Internal/IKafkaCluster.cs
@@ -19,12 +19,14 @@
using MASES.EntityFrameworkCore.KNet.Infrastructure.Internal;
using MASES.EntityFrameworkCore.KNet.Serdes.Internal;
using MASES.EntityFrameworkCore.KNet.ValueGeneration.Internal;
+using MASES.KNet;
using MASES.KNet.Producer;
+using MASES.KNet.Replicator;
using Org.Apache.Kafka.Clients.Producer;
namespace MASES.EntityFrameworkCore.KNet.Storage.Internal;
-public interface IKafkaCluster
+public interface IKafkaCluster :IDisposable
{
bool EnsureDeleted(
IUpdateAdapterFactory updateAdapterFactory,
@@ -46,6 +48,8 @@ bool EnsureConnected(
IKafkaSerdesEntityType CreateSerdes(IEntityType entityType);
+ IKNetCompactedReplicator CreateCompactedReplicator(IEntityType entityType);
+
IProducer CreateProducer(IEntityType entityType);
IEnumerable GetData(IEntityType entityType);
diff --git a/src/net/KEFCore/Storage/Internal/IKafkaDatabase.cs b/src/net/KEFCore/Storage/Internal/IKafkaDatabase.cs
index 6af8feee..e2aee765 100644
--- a/src/net/KEFCore/Storage/Internal/IKafkaDatabase.cs
+++ b/src/net/KEFCore/Storage/Internal/IKafkaDatabase.cs
@@ -18,7 +18,7 @@
namespace MASES.EntityFrameworkCore.KNet.Storage.Internal;
-public interface IKafkaDatabase : IDatabase
+public interface IKafkaDatabase : IDatabase, IDisposable
{
IKafkaCluster Cluster { get; }
diff --git a/src/net/KEFCore/Storage/Internal/IKafkaRowBag.cs b/src/net/KEFCore/Storage/Internal/IKafkaRowBag.cs
new file mode 100644
index 00000000..33e96f29
--- /dev/null
+++ b/src/net/KEFCore/Storage/Internal/IKafkaRowBag.cs
@@ -0,0 +1,32 @@
+/*
+* Copyright 2022 MASES s.r.l.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*
+* Refer to LICENSE for more information.
+*/
+
+#nullable enable
+
+using MASES.EntityFrameworkCore.KNet.ValueGeneration.Internal;
+using Java.Util.Concurrent;
+using Org.Apache.Kafka.Clients.Producer;
+using MASES.KNet.Producer;
+using MASES.EntityFrameworkCore.KNet.Serdes.Internal;
+
+namespace MASES.EntityFrameworkCore.KNet.Storage.Internal;
+
+public interface IKafkaRowBag
+{
+ IUpdateEntry UpdateEntry { get; }
+}
diff --git a/src/net/KEFCore/Storage/Internal/IKafkaTable.cs b/src/net/KEFCore/Storage/Internal/IKafkaTable.cs
index 78e97717..e584c7bc 100644
--- a/src/net/KEFCore/Storage/Internal/IKafkaTable.cs
+++ b/src/net/KEFCore/Storage/Internal/IKafkaTable.cs
@@ -25,7 +25,7 @@
namespace MASES.EntityFrameworkCore.KNet.Storage.Internal;
-public interface IKafkaTable
+public interface IKafkaTable : IDisposable
{
IReadOnlyList