Replace BLite with Surreal embedded persistence
All checks were successful
NuGet Package Publish / nuget (push) Successful in 1m21s
All checks were successful
NuGet Package Publish / nuget (push) Successful in 1m21s
This commit is contained in:
@@ -3,90 +3,125 @@ using Microsoft.Extensions.Logging;
|
||||
using ZB.MOM.WW.CBDDC.Core.Network;
|
||||
using ZB.MOM.WW.CBDDC.Core.Storage;
|
||||
using ZB.MOM.WW.CBDDC.Core.Sync;
|
||||
using ZB.MOM.WW.CBDDC.Persistence.BLite;
|
||||
using ZB.MOM.WW.CBDDC.Persistence.Surreal;
|
||||
|
||||
namespace ZB.MOM.WW.CBDDC.Sample.Console;
|
||||
|
||||
/// <summary>
|
||||
/// Document store implementation for CBDDC Sample using BLite persistence.
|
||||
/// Extends BLiteDocumentStore to automatically handle Oplog creation via CDC.
|
||||
/// Surreal-backed document store for the sample app.
|
||||
/// </summary>
|
||||
public class SampleDocumentStore : BLiteDocumentStore<SampleDbContext>
|
||||
public class SampleDocumentStore : SurrealDocumentStore<SampleDbContext>
|
||||
{
|
||||
private const string UsersCollection = "Users";
|
||||
private const string TodoListsCollection = "TodoLists";
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SampleDocumentStore" /> class.
|
||||
/// </summary>
|
||||
/// <param name="context">The sample database context.</param>
|
||||
/// <param name="configProvider">The peer node configuration provider.</param>
|
||||
/// <param name="vectorClockService">The vector clock service.</param>
|
||||
/// <param name="logger">The optional logger instance.</param>
|
||||
public SampleDocumentStore(
|
||||
SampleDbContext context,
|
||||
IPeerNodeConfigurationProvider configProvider,
|
||||
IVectorClockService vectorClockService,
|
||||
ILogger<SampleDocumentStore>? logger = null)
|
||||
: base(context, configProvider, vectorClockService, new LastWriteWinsConflictResolver(), logger)
|
||||
: base(
|
||||
context,
|
||||
context.SurrealEmbeddedClient,
|
||||
context.SchemaInitializer,
|
||||
configProvider,
|
||||
vectorClockService,
|
||||
new LastWriteWinsConflictResolver(),
|
||||
null,
|
||||
null,
|
||||
logger)
|
||||
{
|
||||
// Register CDC watchers for local change detection
|
||||
// InterestedCollection is automatically populated
|
||||
WatchCollection(UsersCollection, context.Users, u => u.Id);
|
||||
WatchCollection(TodoListsCollection, context.TodoLists, t => t.Id);
|
||||
}
|
||||
|
||||
#region Helper Methods
|
||||
|
||||
private static JsonElement? SerializeEntity<T>(T? entity) where T : class
|
||||
{
|
||||
if (entity == null) return null;
|
||||
return JsonSerializer.SerializeToElement(entity);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Abstract Method Implementations
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async Task ApplyContentToEntityAsync(
|
||||
string collection, string key, JsonElement content, CancellationToken cancellationToken)
|
||||
string collection,
|
||||
string key,
|
||||
JsonElement content,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
UpsertEntity(collection, key, content);
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
await UpsertEntityAsync(collection, key, content, cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async Task ApplyContentToEntitiesBatchAsync(
|
||||
IEnumerable<(string Collection, string Key, JsonElement Content)> documents,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
foreach ((string collection, string key, var content) in documents) UpsertEntity(collection, key, content);
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
foreach ((string collection, string key, var content) in documents)
|
||||
await UpsertEntityAsync(collection, key, content, cancellationToken);
|
||||
}
|
||||
|
||||
private void UpsertEntity(string collection, string key, JsonElement content)
|
||||
protected override async Task<JsonElement?> GetEntityAsJsonAsync(
|
||||
string collection,
|
||||
string key,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
return collection switch
|
||||
{
|
||||
UsersCollection => SerializeEntity(await _context.Users.FindByIdAsync(key, cancellationToken)),
|
||||
TodoListsCollection => SerializeEntity(await _context.TodoLists.FindByIdAsync(key, cancellationToken)),
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
|
||||
protected override async Task RemoveEntityAsync(
|
||||
string collection,
|
||||
string key,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
await DeleteEntityAsync(collection, key, cancellationToken);
|
||||
}
|
||||
|
||||
protected override async Task RemoveEntitiesBatchAsync(
|
||||
IEnumerable<(string Collection, string Key)> documents,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
foreach ((string collection, string key) in documents)
|
||||
await DeleteEntityAsync(collection, key, cancellationToken);
|
||||
}
|
||||
|
||||
protected override async Task<IEnumerable<(string Key, JsonElement Content)>> GetAllEntitiesAsJsonAsync(
|
||||
string collection,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
return collection switch
|
||||
{
|
||||
UsersCollection => (await _context.Users.FindAllAsync(cancellationToken))
|
||||
.Select(u => (u.Id, SerializeEntity(u)!.Value))
|
||||
.ToList(),
|
||||
TodoListsCollection => (await _context.TodoLists.FindAllAsync(cancellationToken))
|
||||
.Select(t => (t.Id, SerializeEntity(t)!.Value))
|
||||
.ToList(),
|
||||
_ => []
|
||||
};
|
||||
}
|
||||
|
||||
private async Task UpsertEntityAsync(
|
||||
string collection,
|
||||
string key,
|
||||
JsonElement content,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
switch (collection)
|
||||
{
|
||||
case UsersCollection:
|
||||
var user = content.Deserialize<User>()!;
|
||||
var user = content.Deserialize<User>() ?? throw new InvalidOperationException("Failed to deserialize user.");
|
||||
user.Id = key;
|
||||
var existingUser = _context.Users.Find(u => u.Id == key).FirstOrDefault();
|
||||
if (existingUser != null)
|
||||
_context.Users.Update(user);
|
||||
if (await _context.Users.FindByIdAsync(key, cancellationToken) == null)
|
||||
await _context.Users.InsertAsync(user, cancellationToken);
|
||||
else
|
||||
_context.Users.Insert(user);
|
||||
await _context.Users.UpdateAsync(user, cancellationToken);
|
||||
break;
|
||||
|
||||
case TodoListsCollection:
|
||||
var todoList = content.Deserialize<TodoList>()!;
|
||||
todoList.Id = key;
|
||||
var existingTodoList = _context.TodoLists.Find(t => t.Id == key).FirstOrDefault();
|
||||
if (existingTodoList != null)
|
||||
_context.TodoLists.Update(todoList);
|
||||
var todo = content.Deserialize<TodoList>() ??
|
||||
throw new InvalidOperationException("Failed to deserialize todo list.");
|
||||
todo.Id = key;
|
||||
if (await _context.TodoLists.FindByIdAsync(key, cancellationToken) == null)
|
||||
await _context.TodoLists.InsertAsync(todo, cancellationToken);
|
||||
else
|
||||
_context.TodoLists.Insert(todoList);
|
||||
await _context.TodoLists.UpdateAsync(todo, cancellationToken);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -94,43 +129,15 @@ public class SampleDocumentStore : BLiteDocumentStore<SampleDbContext>
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Task<JsonElement?> GetEntityAsJsonAsync(
|
||||
string collection, string key, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult(collection switch
|
||||
{
|
||||
UsersCollection => SerializeEntity(_context.Users.Find(u => u.Id == key).FirstOrDefault()),
|
||||
TodoListsCollection => SerializeEntity(_context.TodoLists.Find(t => t.Id == key).FirstOrDefault()),
|
||||
_ => null
|
||||
});
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async Task RemoveEntityAsync(
|
||||
string collection, string key, CancellationToken cancellationToken)
|
||||
{
|
||||
DeleteEntity(collection, key);
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async Task RemoveEntitiesBatchAsync(
|
||||
IEnumerable<(string Collection, string Key)> documents, CancellationToken cancellationToken)
|
||||
{
|
||||
foreach ((string collection, string key) in documents) DeleteEntity(collection, key);
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
|
||||
private void DeleteEntity(string collection, string key)
|
||||
private async Task DeleteEntityAsync(string collection, string key, CancellationToken cancellationToken)
|
||||
{
|
||||
switch (collection)
|
||||
{
|
||||
case UsersCollection:
|
||||
_context.Users.Delete(key);
|
||||
await _context.Users.DeleteAsync(key, cancellationToken);
|
||||
break;
|
||||
case TodoListsCollection:
|
||||
_context.TodoLists.Delete(key);
|
||||
await _context.TodoLists.DeleteAsync(key, cancellationToken);
|
||||
break;
|
||||
default:
|
||||
_logger.LogWarning("Attempted to remove entity from unsupported collection: {Collection}", collection);
|
||||
@@ -138,21 +145,8 @@ public class SampleDocumentStore : BLiteDocumentStore<SampleDbContext>
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async Task<IEnumerable<(string Key, JsonElement Content)>> GetAllEntitiesAsJsonAsync(
|
||||
string collection, CancellationToken cancellationToken)
|
||||
private static JsonElement? SerializeEntity<T>(T? entity) where T : class
|
||||
{
|
||||
return await Task.Run(() => collection switch
|
||||
{
|
||||
UsersCollection => _context.Users.FindAll()
|
||||
.Select(u => (u.Id, SerializeEntity(u)!.Value)),
|
||||
|
||||
TodoListsCollection => _context.TodoLists.FindAll()
|
||||
.Select(t => (t.Id, SerializeEntity(t)!.Value)),
|
||||
|
||||
_ => Enumerable.Empty<(string, JsonElement)>()
|
||||
}, cancellationToken);
|
||||
return entity == null ? null : JsonSerializer.SerializeToElement(entity);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user