Add CultMesh durable shard log defaults
This commit is contained in:
parent
c2b2239853
commit
8fc548e452
5 changed files with 128 additions and 4 deletions
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using GameCult.Caching;
|
||||
using GameCult.Caching.MessagePack;
|
||||
|
|
@ -31,6 +32,16 @@ namespace GameCult.Mesh
|
|||
/// </summary>
|
||||
public CultNetDatabaseOptions? DatabaseOptions { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether CultMesh should attach a file-backed authoritative shard-log store when none is supplied.
|
||||
/// </summary>
|
||||
public bool EnableDurableShardLogs { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the directory used for file-backed authoritative shard logs.
|
||||
/// </summary>
|
||||
public string? ShardLogPath { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the database server bridge options used for shard routing and forwarding.
|
||||
/// </summary>
|
||||
|
|
@ -41,18 +52,54 @@ namespace GameCult.Mesh
|
|||
/// </summary>
|
||||
public Action<Server>? ConfigureServer { get; set; }
|
||||
|
||||
internal CultNetHostOptions ToCultNetOptions()
|
||||
internal CultNetHostOptions ToCultNetOptions(string cachePath)
|
||||
{
|
||||
return new CultNetHostOptions
|
||||
{
|
||||
CacheOptions = CacheOptions,
|
||||
Security = Security,
|
||||
StartServer = StartServer,
|
||||
DatabaseOptions = DatabaseOptions,
|
||||
DatabaseOptions = CreateDatabaseOptions(cachePath),
|
||||
DatabaseServerOptions = DatabaseServerOptions,
|
||||
ConfigureServer = ConfigureServer
|
||||
};
|
||||
}
|
||||
|
||||
private CultNetDatabaseOptions? CreateDatabaseOptions(string cachePath)
|
||||
{
|
||||
if (!EnableDurableShardLogs)
|
||||
{
|
||||
return DatabaseOptions;
|
||||
}
|
||||
|
||||
var source = DatabaseOptions ?? new CultNetDatabaseOptions();
|
||||
if (source.MutationLogStore != null)
|
||||
{
|
||||
return source;
|
||||
}
|
||||
|
||||
return new CultNetDatabaseOptions
|
||||
{
|
||||
RuntimeId = source.RuntimeId,
|
||||
Shards = source.Shards,
|
||||
ClientAuthorityScopes = source.ClientAuthorityScopes,
|
||||
DocumentRegistry = source.DocumentRegistry,
|
||||
MutationLogStore = new CultNetFileShardMutationLogStore(ResolveShardLogPath(cachePath))
|
||||
};
|
||||
}
|
||||
|
||||
private string ResolveShardLogPath(string cachePath)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(ShardLogPath))
|
||||
{
|
||||
return ShardLogPath!;
|
||||
}
|
||||
|
||||
var fullPath = Path.GetFullPath(cachePath);
|
||||
var directory = Path.GetDirectoryName(fullPath) ?? Directory.GetCurrentDirectory();
|
||||
var name = Path.GetFileNameWithoutExtension(fullPath);
|
||||
return Path.Combine(directory, name + ".cultmesh", "shard-logs");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -127,7 +174,7 @@ namespace GameCult.Mesh
|
|||
{
|
||||
var host = await CultNetLocal.CreateHostAsync(
|
||||
cachePath,
|
||||
(options ?? new CultMeshNodeOptions()).ToCultNetOptions()).ConfigureAwait(false);
|
||||
(options ?? new CultMeshNodeOptions()).ToCultNetOptions(cachePath)).ConfigureAwait(false);
|
||||
return new CultMeshNode(host);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,19 @@ var player = await node.Database.GetAsync<PlayerData>(playerKey);
|
|||
await node.Database.PutAsync(playerKey, player);
|
||||
```
|
||||
|
||||
Enable durable authoritative shard logs when a node should serve replica
|
||||
catch-up after restart:
|
||||
|
||||
```csharp
|
||||
using var node = await CultMesh.StartNodeAsync("world.ccmp", new CultMeshNodeOptions
|
||||
{
|
||||
EnableDurableShardLogs = true
|
||||
});
|
||||
```
|
||||
|
||||
If `ShardLogPath` is omitted, CultMesh stores logs beside the cache under
|
||||
`world.cultmesh/shard-logs`.
|
||||
|
||||
## Verses
|
||||
|
||||
A Verse is a rule-bearing consensus graph. Aetheria Main can be a
|
||||
|
|
|
|||
|
|
@ -21,6 +21,11 @@ Namespace: `GameCult.Mesh`
|
|||
- `Database`: the distributed realtime database facade
|
||||
- `DatabaseServer`: the schema-v0 database bridge
|
||||
|
||||
`CultMeshNodeOptions.EnableDurableShardLogs` attaches a file-backed
|
||||
authoritative shard-log store when `DatabaseOptions.MutationLogStore` is not
|
||||
already supplied. `ShardLogPath` can override the location; otherwise CultMesh
|
||||
uses a `.cultmesh/shard-logs` directory beside the cache file.
|
||||
|
||||
The package home and primary entrypoint are `GameCult.Mesh` / `CultMesh`.
|
||||
Some lower-level database and wire-contract types still retain `CultNet` names
|
||||
because they are shared with the transport package. Treat those as plumbing,
|
||||
|
|
|
|||
|
|
@ -67,6 +67,8 @@ Implemented:
|
|||
- shard-bounded snapshots with shard id, epoch, and represented log sequence
|
||||
- replica snapshot recovery after compacted history, including local shard
|
||||
replacement and cursor advancement
|
||||
- CultMesh node option for file-backed authoritative shard logs with a default
|
||||
`.cultmesh/shard-logs` path beside the cache file
|
||||
- client-authority scopes for locally predicted input documents
|
||||
- predicted and reconciled database change kinds for client-side prediction
|
||||
- simulation witness observations for mesh-side opinions about frame facts
|
||||
|
|
@ -119,7 +121,6 @@ Not implemented yet:
|
|||
|
||||
Add discovery fanout and operational polish:
|
||||
|
||||
- expose operator-facing defaults for durable shard-log paths in CultMesh nodes
|
||||
- add Verse discovery over the schema-v0 mesh lane
|
||||
- add snapshot throttling and size limits for public mesh edges
|
||||
- after that, add simulation-frame rollback/resimulation helpers around
|
||||
|
|
|
|||
|
|
@ -792,6 +792,64 @@ namespace GameCult.Networking.Tests
|
|||
Assert.That(incompatible.CanTransferFrom(aetheria), Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task CultMeshNode_CanEnable_DurableShardLogs_WithDefaultPath()
|
||||
{
|
||||
var rootPath = Path.Combine(TestContext.CurrentContext.WorkDirectory, "cultmesh-node", Guid.NewGuid().ToString("N"));
|
||||
Directory.CreateDirectory(rootPath);
|
||||
var cachePath = Path.Combine(rootPath, "world.ccmp");
|
||||
var cache = new CultCache();
|
||||
var schemaId = cache.Registry.GetRequired<PlayerData>().SchemaId;
|
||||
var registry = new CultNetDocumentRegistry(cache.Registry)
|
||||
.Register(CultNetDocumentBinding.ForDocument<PlayerData>(
|
||||
cache.Registry,
|
||||
payloadSerializer: SerializePlayerDataPayload,
|
||||
payloadDeserializer: DeserializePlayerDataPayload));
|
||||
var options = new CultMeshNodeOptions
|
||||
{
|
||||
StartServer = false,
|
||||
EnableDurableShardLogs = true,
|
||||
DatabaseOptions = new CultNetDatabaseOptions
|
||||
{
|
||||
DocumentRegistry = registry,
|
||||
Shards =
|
||||
[
|
||||
new CultNetShardDescriptor(
|
||||
"players-mesh-default-log",
|
||||
"mesh-runtime",
|
||||
epoch: 1,
|
||||
isPrimary: true,
|
||||
schemaIds: [schemaId],
|
||||
keyPrefix: "player:")
|
||||
]
|
||||
}
|
||||
};
|
||||
var key = new CultRecordKey("player:mesh-log");
|
||||
var player = new PlayerData
|
||||
{
|
||||
PlayerId = Guid.NewGuid(),
|
||||
Email = "mesh-log@example.test",
|
||||
PasswordHash = "hash",
|
||||
Username = "MeshLog"
|
||||
};
|
||||
|
||||
using (var node = await CultMesh.CreateNodeAsync(cachePath, options))
|
||||
{
|
||||
await node.Database.PutAsync(key, player);
|
||||
}
|
||||
|
||||
using var restarted = await CultMesh.CreateNodeAsync(cachePath, options);
|
||||
var response = restarted.DatabaseServer.CreateShardLogResponse(new CultNetShardLogRequestMessage
|
||||
{
|
||||
ShardId = "players-mesh-default-log"
|
||||
});
|
||||
|
||||
Assert.That(response.Entries, Has.Length.EqualTo(1));
|
||||
Assert.That(response.Entries[0].Put, Is.Not.Null);
|
||||
Assert.That(response.Entries[0].Put!.Document.RecordKey, Is.EqualTo(key.Value));
|
||||
Assert.That(Directory.Exists(Path.Combine(rootPath, "world.cultmesh", "shard-logs")), Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task CultNetDatabase_Appends_PerShardMutationLog()
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in a new issue