Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce keyed service resolution for RocksDb stores #32

Merged
merged 1 commit into from
Jan 1, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,15 @@ rocksDbBuilder.AddStore<string, User, UsersStore>("users-store");

This registers an instance of `UsersStore` with RocksDb under the name "users-store".

#### Keyed Service Resolution
You can also resolve your store as a keyed service using the column family name:

```csharp
var usersStore = serviceProvider.GetRequiredKeyedService<UsersStore>("users-store");
```

This approach allows you to register and retrieve multiple stores of the same type, each differentiated by their column family name.

### Use your store

Once you have registered your store, you can use it to add, get, and remove data from RocksDb. For example:
Expand Down
10 changes: 7 additions & 3 deletions src/RocksDb.Extensions/IRocksDbBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,18 @@ public interface IRocksDbBuilder
/// <summary>
/// Adds a RocksDB store to the builder for the specified column family.
/// </summary>
/// <param name="columnFamily"></param>
/// <param name="columnFamily">The name of the column family to associate with the store.</param>
/// <typeparam name="TKey">The type of the store's key.</typeparam>
/// <typeparam name="TValue">The type of the store's value.</typeparam>
/// <typeparam name="TStore">The type of the store to add.</typeparam>
/// <returns>The builder instance for method chaining</returns>
/// <returns>The builder instance for method chaining.</returns>
/// <exception cref="InvalidOperationException">Thrown if the specified column family is already registered.</exception>
/// <remarks>
/// The <typeparamref name="TStore"/> type must be a concrete implementation of the abstract class
/// <see cref="RocksDbStore{TKey,TValue}"/>.
/// <see cref="RocksDbStore{TKey,TValue}"/>. Each store is registered uniquely based on its column family name.
///
/// Stores can also be resolved as keyed services using their associated column family name.
/// Use <c>GetRequiredKeyedService&lt;TStore&gt;(columnFamily)</c> to retrieve a specific store instance.
/// </remarks>
IRocksDbBuilder AddStore<TKey, TValue, TStore>(string columnFamily) where TStore : RocksDbStore<TKey, TValue>;
}
2 changes: 1 addition & 1 deletion src/RocksDb.Extensions/RocksDb.Extensions.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

<ItemGroup>
<PackageReference Include="CommunityToolkit.HighPerformance" Version="8.2.2" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.0" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="6.0.0" />
<PackageReference Include="RocksDB" Version="8.11.3.46984" />
</ItemGroup>
Expand Down
8 changes: 6 additions & 2 deletions src/RocksDb.Extensions/RocksDbBuilder.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options;

namespace RocksDb.Extensions;
Expand All @@ -22,8 +23,8 @@ public IRocksDbBuilder AddStore<TKey, TValue, TStore>(string columnFamily) where
}

_ = _serviceCollection.Configure<RocksDbOptions>(options => { options.ColumnFamilies.Add(columnFamily); });

_ = _serviceCollection.AddSingleton(provider =>
_serviceCollection.AddKeyedSingleton<TStore>(columnFamily, (provider, _) =>
{
var rocksDbContext = provider.GetRequiredService<RocksDbContext>();
var columnFamilyHandle = rocksDbContext.Db.GetColumnFamily(columnFamily);
Expand All @@ -38,6 +39,9 @@ public IRocksDbBuilder AddStore<TKey, TValue, TStore>(string columnFamily) where
);
return ActivatorUtilities.CreateInstance<TStore>(provider, rocksDbAccessor);
});

_serviceCollection.TryAddSingleton(typeof(TStore), provider => provider.GetRequiredKeyedService<TStore>(columnFamily));

return this;
}

Expand Down
80 changes: 80 additions & 0 deletions test/RocksDb.Extensions.Tests/KeyedStoreTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
using Microsoft.Extensions.DependencyInjection;
using NUnit.Framework;
using RocksDb.Extensions.Protobuf;
using RocksDb.Extensions.Tests.Utils;

namespace RocksDb.Extensions.Tests;

public class KeyedStoreTests
{
[Test]
public void should_resolve_rocksdb_stores_as_keyed_services_when_registered_under_different_column_names()
{
// Arrange
using var testFixture = TestFixture.Create(rockDb =>
{
_ = rockDb.AddStore<CacheKey, CacheValue, RocksDbGenericStore<CacheKey, CacheValue>>("my-store-1");
_ = rockDb.AddStore<CacheKey, CacheValue, RocksDbGenericStore<CacheKey, CacheValue>>("my-store-2");
}, options =>
{
options.SerializerFactories.Clear();
options.SerializerFactories.Add(new ProtobufSerializerFactory());
});

// Act
var store1 = testFixture.ServiceProvider.GetRequiredKeyedService<RocksDbGenericStore<CacheKey, CacheValue>>("my-store-1");
var store2 = testFixture.ServiceProvider.GetRequiredKeyedService<RocksDbGenericStore<CacheKey, CacheValue>>("my-store-2");

// Assert
Assert.Multiple(() =>
{
Assert.That(store1, Is.Not.Null);
Assert.That(store2, Is.Not.Null);
Assert.That(ReferenceEquals(store1, store2), Is.False);
});
}

[Test]
public void should_throw_when_resolving_non_existent_store()
{
// Arrange
using var testFixture = TestFixture.Create(rockDb =>
{
_ = rockDb.AddStore<CacheKey, CacheValue, RocksDbGenericStore<CacheKey, CacheValue>>("my-store-1");
}, options =>
{
options.SerializerFactories.Clear();
options.SerializerFactories.Add(new ProtobufSerializerFactory());
});

// Act & Assert
Assert.Throws<InvalidOperationException>(() => testFixture.ServiceProvider.GetRequiredKeyedService<RocksDbGenericStore<CacheKey, CacheValue>>("non-existent-store"));
}

[Test]
public void should_resolve_default_store_as_first_registered_keyed_service()
{
// Arrange
using var testFixture = TestFixture.Create(rockDb =>
{
_ = rockDb.AddStore<CacheKey, CacheValue, RocksDbGenericStore<CacheKey, CacheValue>>("my-store-1");
_ = rockDb.AddStore<CacheKey, CacheValue, RocksDbGenericStore<CacheKey, CacheValue>>("my-store-2");
}, options =>
{
options.SerializerFactories.Clear();
options.SerializerFactories.Add(new ProtobufSerializerFactory());
});

// Act
var store1 = testFixture.ServiceProvider.GetRequiredKeyedService<RocksDbGenericStore<CacheKey, CacheValue>>("my-store-1");
var store2 = testFixture.ServiceProvider.GetRequiredKeyedService<RocksDbGenericStore<CacheKey, CacheValue>>("my-store-2");
var store = testFixture.ServiceProvider.GetRequiredService<RocksDbGenericStore<CacheKey, CacheValue>>();

// Assert
Assert.Multiple(() =>
{
Assert.That(ReferenceEquals(store1, store), Is.True);
Assert.That(ReferenceEquals(store2, store), Is.False);
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.1" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" />
<PackageReference Include="NScenario" Version="4.3.0" />
<PackageReference Include="NUnit" Version="3.13.3" />
Expand Down
2 changes: 2 additions & 0 deletions test/RocksDb.Extensions.Tests/Utils/TestFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ public T GetStore<T>() where T : notnull
return _serviceProvider.GetRequiredService<T>();
}

public IServiceProvider ServiceProvider => _serviceProvider;

public void Dispose()
{
_serviceProvider.Dispose();
Expand Down
Loading