Add XML docs required by CommentChecker fixes
All checks were successful
NuGet Package Publish / nuget (push) Successful in 1m13s
All checks were successful
NuGet Package Publish / nuget (push) Successful in 1m13s
This commit is contained in:
@@ -24,6 +24,11 @@ public sealed class FeatureFlagOplogStore : IOplogStore
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="FeatureFlagOplogStore" /> class.
|
/// Initializes a new instance of the <see cref="FeatureFlagOplogStore" /> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="surreal">The Surreal-backed oplog store.</param>
|
||||||
|
/// <param name="lmdb">The LMDB-backed oplog store.</param>
|
||||||
|
/// <param name="flags">Feature flags controlling migration and routing behavior.</param>
|
||||||
|
/// <param name="telemetry">Optional telemetry collector for migration metrics.</param>
|
||||||
|
/// <param name="logger">Optional logger for routing and fallback diagnostics.</param>
|
||||||
public FeatureFlagOplogStore(
|
public FeatureFlagOplogStore(
|
||||||
SurrealOplogStore surreal,
|
SurrealOplogStore surreal,
|
||||||
LmdbOplogStore lmdb,
|
LmdbOplogStore lmdb,
|
||||||
|
|||||||
@@ -18,6 +18,9 @@ public sealed class LmdbOplogBackfillTool
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="LmdbOplogBackfillTool" /> class.
|
/// Initializes a new instance of the <see cref="LmdbOplogBackfillTool" /> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="source">The Surreal oplog source.</param>
|
||||||
|
/// <param name="destination">The LMDB oplog destination.</param>
|
||||||
|
/// <param name="logger">Optional logger instance.</param>
|
||||||
public LmdbOplogBackfillTool(
|
public LmdbOplogBackfillTool(
|
||||||
SurrealOplogStore source,
|
SurrealOplogStore source,
|
||||||
LmdbOplogStore destination,
|
LmdbOplogStore destination,
|
||||||
@@ -31,6 +34,8 @@ public sealed class LmdbOplogBackfillTool
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Backfills one dataset from Surreal to LMDB and validates parity.
|
/// Backfills one dataset from Surreal to LMDB and validates parity.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="datasetId">Dataset identifier to migrate.</param>
|
||||||
|
/// <param name="cancellationToken">Cancellation token.</param>
|
||||||
public async Task<LmdbOplogBackfillReport> BackfillAsync(
|
public async Task<LmdbOplogBackfillReport> BackfillAsync(
|
||||||
string datasetId,
|
string datasetId,
|
||||||
CancellationToken cancellationToken = default)
|
CancellationToken cancellationToken = default)
|
||||||
@@ -61,6 +66,8 @@ public sealed class LmdbOplogBackfillTool
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Validates parity only without running a backfill merge.
|
/// Validates parity only without running a backfill merge.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="datasetId">Dataset identifier to validate.</param>
|
||||||
|
/// <param name="cancellationToken">Cancellation token.</param>
|
||||||
public async Task<LmdbOplogBackfillReport> ValidateParityAsync(
|
public async Task<LmdbOplogBackfillReport> ValidateParityAsync(
|
||||||
string datasetId,
|
string datasetId,
|
||||||
CancellationToken cancellationToken = default)
|
CancellationToken cancellationToken = default)
|
||||||
@@ -73,6 +80,8 @@ public sealed class LmdbOplogBackfillTool
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Backfills and throws when parity validation fails.
|
/// Backfills and throws when parity validation fails.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="datasetId">Dataset identifier to backfill.</param>
|
||||||
|
/// <param name="cancellationToken">Cancellation token.</param>
|
||||||
public async Task<LmdbOplogBackfillReport> BackfillOrThrowAsync(
|
public async Task<LmdbOplogBackfillReport> BackfillOrThrowAsync(
|
||||||
string datasetId,
|
string datasetId,
|
||||||
CancellationToken cancellationToken = default)
|
CancellationToken cancellationToken = default)
|
||||||
|
|||||||
@@ -35,6 +35,12 @@ public sealed class LmdbOplogStore : OplogStore, IDisposable
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="LmdbOplogStore" /> class.
|
/// Initializes a new instance of the <see cref="LmdbOplogStore" /> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="documentStore">Document store used for snapshot metadata persistence.</param>
|
||||||
|
/// <param name="conflictResolver">Conflict resolver used for resolving oplog merges.</param>
|
||||||
|
/// <param name="vectorClockService">Vector-clock service used for dataset ordering.</param>
|
||||||
|
/// <param name="options">Configuration options for LMDB storage.</param>
|
||||||
|
/// <param name="snapshotMetadataStore">Optional snapshot metadata store.</param>
|
||||||
|
/// <param name="logger">Optional logger for LMDB diagnostics.</param>
|
||||||
public LmdbOplogStore(
|
public LmdbOplogStore(
|
||||||
IDocumentStore documentStore,
|
IDocumentStore documentStore,
|
||||||
IConflictResolver conflictResolver,
|
IConflictResolver conflictResolver,
|
||||||
@@ -342,6 +348,8 @@ public sealed class LmdbOplogStore : OplogStore, IDisposable
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Drops all oplog data for the specified dataset.
|
/// Drops all oplog data for the specified dataset.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="datasetId">The dataset identifier.</param>
|
||||||
|
/// <param name="cancellationToken">Cancellation token.</param>
|
||||||
public async Task DropAsync(string datasetId, CancellationToken cancellationToken = default)
|
public async Task DropAsync(string datasetId, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
string normalizedDatasetId = NormalizeDatasetId(datasetId);
|
string normalizedDatasetId = NormalizeDatasetId(datasetId);
|
||||||
@@ -373,6 +381,8 @@ public sealed class LmdbOplogStore : OplogStore, IDisposable
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Exports all oplog entries for a dataset.
|
/// Exports all oplog entries for a dataset.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="datasetId">The dataset identifier.</param>
|
||||||
|
/// <param name="cancellationToken">Cancellation token.</param>
|
||||||
public Task<IEnumerable<OplogEntry>> ExportAsync(string datasetId, CancellationToken cancellationToken = default)
|
public Task<IEnumerable<OplogEntry>> ExportAsync(string datasetId, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
@@ -408,6 +418,9 @@ public sealed class LmdbOplogStore : OplogStore, IDisposable
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Imports oplog entries for a dataset (upsert semantics).
|
/// Imports oplog entries for a dataset (upsert semantics).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="items">Entries to import.</param>
|
||||||
|
/// <param name="datasetId">The dataset identifier.</param>
|
||||||
|
/// <param name="cancellationToken">Cancellation token.</param>
|
||||||
public Task ImportAsync(
|
public Task ImportAsync(
|
||||||
IEnumerable<OplogEntry> items,
|
IEnumerable<OplogEntry> items,
|
||||||
string datasetId,
|
string datasetId,
|
||||||
@@ -425,6 +438,9 @@ public sealed class LmdbOplogStore : OplogStore, IDisposable
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Merges oplog entries into a dataset (dedupe by hash).
|
/// Merges oplog entries into a dataset (dedupe by hash).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="items">Entries to merge.</param>
|
||||||
|
/// <param name="datasetId">The dataset identifier.</param>
|
||||||
|
/// <param name="cancellationToken">Cancellation token.</param>
|
||||||
public Task MergeAsync(
|
public Task MergeAsync(
|
||||||
IEnumerable<OplogEntry> items,
|
IEnumerable<OplogEntry> items,
|
||||||
string datasetId,
|
string datasetId,
|
||||||
@@ -626,6 +642,8 @@ public sealed class LmdbOplogStore : OplogStore, IDisposable
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns index-level diagnostics for a dataset, useful in contract/unit tests.
|
/// Returns index-level diagnostics for a dataset, useful in contract/unit tests.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="datasetId">The dataset identifier.</param>
|
||||||
|
/// <param name="cancellationToken">Cancellation token.</param>
|
||||||
public Task<LmdbOplogIndexDiagnostics> GetIndexDiagnosticsAsync(
|
public Task<LmdbOplogIndexDiagnostics> GetIndexDiagnosticsAsync(
|
||||||
string datasetId,
|
string datasetId,
|
||||||
CancellationToken cancellationToken = default)
|
CancellationToken cancellationToken = default)
|
||||||
@@ -1360,15 +1378,54 @@ public sealed class LmdbOplogStore : OplogStore, IDisposable
|
|||||||
|
|
||||||
private sealed class OplogEntryDto
|
private sealed class OplogEntryDto
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Dataset identifier for the serialized oplog entry.
|
||||||
|
/// </summary>
|
||||||
public string DatasetId { get; set; } = global::ZB.MOM.WW.CBDDC.Core.DatasetId.Primary;
|
public string DatasetId { get; set; } = global::ZB.MOM.WW.CBDDC.Core.DatasetId.Primary;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Entry collection name.
|
||||||
|
/// </summary>
|
||||||
public string Collection { get; set; } = string.Empty;
|
public string Collection { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Entry key within the collection.
|
||||||
|
/// </summary>
|
||||||
public string Key { get; set; } = string.Empty;
|
public string Key { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Operation performed for this entry.
|
||||||
|
/// </summary>
|
||||||
public OperationType Operation { get; set; }
|
public OperationType Operation { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Serialized payload for the entry.
|
||||||
|
/// </summary>
|
||||||
public JsonElement? Payload { get; set; }
|
public JsonElement? Payload { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Physical time component of the HLC timestamp.
|
||||||
|
/// </summary>
|
||||||
public long PhysicalTime { get; set; }
|
public long PhysicalTime { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Logical counter component of the HLC timestamp.
|
||||||
|
/// </summary>
|
||||||
public int LogicalCounter { get; set; }
|
public int LogicalCounter { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Logical-clock node identifier.
|
||||||
|
/// </summary>
|
||||||
public string NodeId { get; set; } = string.Empty;
|
public string NodeId { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Previous hash in the oplog chain.
|
||||||
|
/// </summary>
|
||||||
public string PreviousHash { get; set; } = string.Empty;
|
public string PreviousHash { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Entry hash.
|
||||||
|
/// </summary>
|
||||||
public string Hash { get; set; } = string.Empty;
|
public string Hash { get; set; } = string.Empty;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1376,10 +1433,60 @@ public sealed class LmdbOplogStore : OplogStore, IDisposable
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Dataset-scoped LMDB oplog index counts.
|
/// Dataset-scoped LMDB oplog index counts.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly record struct LmdbOplogIndexDiagnostics(
|
public readonly record struct LmdbOplogIndexDiagnostics
|
||||||
string DatasetId,
|
{
|
||||||
long OplogByHashCount,
|
/// <summary>
|
||||||
long OplogByHlcCount,
|
/// The dataset identifier for these diagnostics.
|
||||||
long OplogByNodeHlcCount,
|
/// </summary>
|
||||||
long OplogPrevToHashCount,
|
public string DatasetId { get; init; }
|
||||||
long OplogNodeHeadCount);
|
|
||||||
|
/// <summary>
|
||||||
|
/// Count of entries in the hash index.
|
||||||
|
/// </summary>
|
||||||
|
public long OplogByHashCount { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Count of entries in the HLC index.
|
||||||
|
/// </summary>
|
||||||
|
public long OplogByHlcCount { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Count of entries in the per-node HLC index.
|
||||||
|
/// </summary>
|
||||||
|
public long OplogByNodeHlcCount { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Count of entries in the previous-hash index.
|
||||||
|
/// </summary>
|
||||||
|
public long OplogPrevToHashCount { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Count of entries tracked in the node head index.
|
||||||
|
/// </summary>
|
||||||
|
public long OplogNodeHeadCount { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a diagnostics snapshot for a dataset.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="datasetId">Dataset identifier for the diagnostics.</param>
|
||||||
|
/// <param name="oplogByHashCount">Count of entries in the hash index.</param>
|
||||||
|
/// <param name="oplogByHlcCount">Count of entries in the HLC index.</param>
|
||||||
|
/// <param name="oplogByNodeHlcCount">Count of entries in the per-node HLC index.</param>
|
||||||
|
/// <param name="oplogPrevToHashCount">Count of entries in the previous-hash index.</param>
|
||||||
|
/// <param name="oplogNodeHeadCount">Count of node-head entries.</param>
|
||||||
|
public LmdbOplogIndexDiagnostics(
|
||||||
|
string datasetId,
|
||||||
|
long oplogByHashCount,
|
||||||
|
long oplogByHlcCount,
|
||||||
|
long oplogByNodeHlcCount,
|
||||||
|
long oplogPrevToHashCount,
|
||||||
|
long oplogNodeHeadCount)
|
||||||
|
{
|
||||||
|
DatasetId = datasetId;
|
||||||
|
OplogByHashCount = oplogByHashCount;
|
||||||
|
OplogByHlcCount = oplogByHlcCount;
|
||||||
|
OplogByNodeHlcCount = oplogByNodeHlcCount;
|
||||||
|
OplogPrevToHashCount = oplogPrevToHashCount;
|
||||||
|
OplogNodeHeadCount = oplogNodeHeadCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ public sealed class OplogMigrationTelemetry
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Records the outcome of one shadow comparison.
|
/// Records the outcome of one shadow comparison.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="isMatch"><see langword="true" /> when source and LMDB entries matched.</param>
|
||||||
public void RecordShadowComparison(bool isMatch)
|
public void RecordShadowComparison(bool isMatch)
|
||||||
{
|
{
|
||||||
Interlocked.Increment(ref _shadowComparisons);
|
Interlocked.Increment(ref _shadowComparisons);
|
||||||
@@ -42,6 +43,8 @@ public sealed class OplogMigrationTelemetry
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Records one reconciliation/backfill run for a dataset.
|
/// Records one reconciliation/backfill run for a dataset.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="datasetId">Dataset identifier for this reconciliation.</param>
|
||||||
|
/// <param name="entriesMerged">Count of entries merged into LMDB.</param>
|
||||||
public void RecordReconciliation(string datasetId, int entriesMerged)
|
public void RecordReconciliation(string datasetId, int entriesMerged)
|
||||||
{
|
{
|
||||||
string normalizedDatasetId = DatasetId.Normalize(datasetId);
|
string normalizedDatasetId = DatasetId.Normalize(datasetId);
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ namespace ZB.MOM.WW.CBDDC.Core.Tests;
|
|||||||
|
|
||||||
public class DatasetAwareModelTests
|
public class DatasetAwareModelTests
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies <see cref="DocumentMetadata" /> defaults to the primary dataset.
|
||||||
|
/// </summary>
|
||||||
[Fact]
|
[Fact]
|
||||||
public void DocumentMetadata_ShouldDefaultDatasetId_ToPrimary()
|
public void DocumentMetadata_ShouldDefaultDatasetId_ToPrimary()
|
||||||
{
|
{
|
||||||
@@ -13,6 +16,9 @@ public class DatasetAwareModelTests
|
|||||||
metadata.DatasetId.ShouldBe(DatasetId.Primary);
|
metadata.DatasetId.ShouldBe(DatasetId.Primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies dataset identifiers survive JSON round-trips for <see cref="DocumentMetadata" />.
|
||||||
|
/// </summary>
|
||||||
[Fact]
|
[Fact]
|
||||||
public void DocumentMetadata_SerializationRoundTrip_ShouldPreserveDatasetId()
|
public void DocumentMetadata_SerializationRoundTrip_ShouldPreserveDatasetId()
|
||||||
{
|
{
|
||||||
@@ -25,6 +31,9 @@ public class DatasetAwareModelTests
|
|||||||
restored.DatasetId.ShouldBe("logs");
|
restored.DatasetId.ShouldBe("logs");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies <see cref="SnapshotMetadata" /> defaults to the primary dataset.
|
||||||
|
/// </summary>
|
||||||
[Fact]
|
[Fact]
|
||||||
public void SnapshotMetadata_ShouldDefaultDatasetId_ToPrimary()
|
public void SnapshotMetadata_ShouldDefaultDatasetId_ToPrimary()
|
||||||
{
|
{
|
||||||
@@ -33,6 +42,9 @@ public class DatasetAwareModelTests
|
|||||||
metadata.DatasetId.ShouldBe(DatasetId.Primary);
|
metadata.DatasetId.ShouldBe(DatasetId.Primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies <see cref="PeerOplogConfirmation" /> defaults to the primary dataset.
|
||||||
|
/// </summary>
|
||||||
[Fact]
|
[Fact]
|
||||||
public void PeerOplogConfirmation_ShouldDefaultDatasetId_ToPrimary()
|
public void PeerOplogConfirmation_ShouldDefaultDatasetId_ToPrimary()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -6,6 +6,9 @@ namespace ZB.MOM.WW.CBDDC.Network.Tests;
|
|||||||
|
|
||||||
public class MultiDatasetRegistrationTests
|
public class MultiDatasetRegistrationTests
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies CBDDC network registration replaces the default orchestrator with multi-dataset wiring.
|
||||||
|
/// </summary>
|
||||||
[Fact]
|
[Fact]
|
||||||
public void AddCBDDCMultiDataset_ShouldRegisterCoordinatorAndReplaceSyncOrchestrator()
|
public void AddCBDDCMultiDataset_ShouldRegisterCoordinatorAndReplaceSyncOrchestrator()
|
||||||
{
|
{
|
||||||
@@ -28,12 +31,18 @@ public class MultiDatasetRegistrationTests
|
|||||||
|
|
||||||
private sealed class TestPeerNodeConfigurationProvider : IPeerNodeConfigurationProvider
|
private sealed class TestPeerNodeConfigurationProvider : IPeerNodeConfigurationProvider
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Raised when the peer configuration changes.
|
||||||
|
/// </summary>
|
||||||
public event PeerNodeConfigurationChangedEventHandler? ConfigurationChanged
|
public event PeerNodeConfigurationChangedEventHandler? ConfigurationChanged
|
||||||
{
|
{
|
||||||
add { }
|
add { }
|
||||||
remove { }
|
remove { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the current peer configuration.
|
||||||
|
/// </summary>
|
||||||
public Task<PeerNodeConfiguration> GetConfiguration()
|
public Task<PeerNodeConfiguration> GetConfiguration()
|
||||||
{
|
{
|
||||||
return Task.FromResult(new PeerNodeConfiguration
|
return Task.FromResult(new PeerNodeConfiguration
|
||||||
|
|||||||
@@ -7,6 +7,9 @@ namespace ZB.MOM.WW.CBDDC.Network.Tests;
|
|||||||
|
|
||||||
public class MultiDatasetSyncOrchestratorTests
|
public class MultiDatasetSyncOrchestratorTests
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies multi-dataset sync is disabled, only the primary context is created.
|
||||||
|
/// </summary>
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Constructor_WhenMultiDatasetDisabled_ShouldOnlyCreatePrimaryContext()
|
public void Constructor_WhenMultiDatasetDisabled_ShouldOnlyCreatePrimaryContext()
|
||||||
{
|
{
|
||||||
@@ -27,6 +30,9 @@ public class MultiDatasetSyncOrchestratorTests
|
|||||||
datasetIds[0].ShouldBe(DatasetId.Primary);
|
datasetIds[0].ShouldBe(DatasetId.Primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies that failures in one orchestrator do not prevent remaining contexts from starting and stopping.
|
||||||
|
/// </summary>
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task StartStop_WhenOneDatasetThrows_ShouldContinueOtherDatasets()
|
public async Task StartStop_WhenOneDatasetThrows_ShouldContinueOtherDatasets()
|
||||||
{
|
{
|
||||||
@@ -82,9 +88,19 @@ public class MultiDatasetSyncOrchestratorTests
|
|||||||
private sealed class TrackingSyncOrchestrator(Exception? startException = null, Exception? stopException = null)
|
private sealed class TrackingSyncOrchestrator(Exception? startException = null, Exception? stopException = null)
|
||||||
: ISyncOrchestrator
|
: ISyncOrchestrator
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Number of times <see cref="Start" /> has been called.
|
||||||
|
/// </summary>
|
||||||
public int StartCalls { get; private set; }
|
public int StartCalls { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Number of times <see cref="Stop" /> has been called.
|
||||||
|
/// </summary>
|
||||||
public int StopCalls { get; private set; }
|
public int StopCalls { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Starts the orchestrator.
|
||||||
|
/// </summary>
|
||||||
public Task Start()
|
public Task Start()
|
||||||
{
|
{
|
||||||
StartCalls++;
|
StartCalls++;
|
||||||
@@ -92,6 +108,9 @@ public class MultiDatasetSyncOrchestratorTests
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Stops the orchestrator.
|
||||||
|
/// </summary>
|
||||||
public Task Stop()
|
public Task Stop()
|
||||||
{
|
{
|
||||||
StopCalls++;
|
StopCalls++;
|
||||||
|
|||||||
@@ -12,6 +12,9 @@ namespace ZB.MOM.WW.CBDDC.Sample.Console.Tests;
|
|||||||
|
|
||||||
public class LmdbOplogMigrationTests
|
public class LmdbOplogMigrationTests
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies dual-write mode writes each entry to both Surreal and LMDB stores.
|
||||||
|
/// </summary>
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task FeatureFlags_DualWrite_WritesToBothStores()
|
public async Task FeatureFlags_DualWrite_WritesToBothStores()
|
||||||
{
|
{
|
||||||
@@ -39,6 +42,9 @@ public class LmdbOplogMigrationTests
|
|||||||
(await lmdbStore.GetEntryByHashAsync(entry.Hash)).ShouldNotBeNull();
|
(await lmdbStore.GetEntryByHashAsync(entry.Hash)).ShouldNotBeNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies preferred LMDB reads reconcile missing LMDB data from Surreal.
|
||||||
|
/// </summary>
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task FeatureFlags_PreferLmdbReads_ReconcilesFromSurrealWhenLmdbMissingEntries()
|
public async Task FeatureFlags_PreferLmdbReads_ReconcilesFromSurrealWhenLmdbMissingEntries()
|
||||||
{
|
{
|
||||||
@@ -78,6 +84,9 @@ public class LmdbOplogMigrationTests
|
|||||||
telemetry.ReconciledEntries.ShouldBeGreaterThanOrEqualTo(1);
|
telemetry.ReconciledEntries.ShouldBeGreaterThanOrEqualTo(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies shadow validation records mismatches when LMDB and Surreal diverge.
|
||||||
|
/// </summary>
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task FeatureFlags_ShadowValidation_RecordsMismatchTelemetry()
|
public async Task FeatureFlags_ShadowValidation_RecordsMismatchTelemetry()
|
||||||
{
|
{
|
||||||
@@ -112,6 +121,9 @@ public class LmdbOplogMigrationTests
|
|||||||
snapshot.ShadowMismatches.ShouldBe(1);
|
snapshot.ShadowMismatches.ShouldBe(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies rollback to Surreal when dual-write is disabled uses Surreal for all writes and reads.
|
||||||
|
/// </summary>
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task FeatureFlags_RollbackToSurreal_UsesSurrealForWritesAndReads()
|
public async Task FeatureFlags_RollbackToSurreal_UsesSurrealForWritesAndReads()
|
||||||
{
|
{
|
||||||
@@ -143,6 +155,9 @@ public class LmdbOplogMigrationTests
|
|||||||
routedRead.Hash.ShouldBe(entry.Hash);
|
routedRead.Hash.ShouldBe(entry.Hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies backfill succeeds and records matching dataset counts.
|
||||||
|
/// </summary>
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task BackfillTool_BackfillAndValidate_ReportsSuccess()
|
public async Task BackfillTool_BackfillAndValidate_ReportsSuccess()
|
||||||
{
|
{
|
||||||
@@ -173,6 +188,9 @@ public class LmdbOplogMigrationTests
|
|||||||
report.DestinationCount.ShouldBe(4);
|
report.DestinationCount.ShouldBe(4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies backfill can target non-primary datasets successfully.
|
||||||
|
/// </summary>
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task BackfillTool_BackfillAndValidate_WorksPerDataset()
|
public async Task BackfillTool_BackfillAndValidate_WorksPerDataset()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ namespace ZB.MOM.WW.CBDDC.Sample.Console.Tests;
|
|||||||
|
|
||||||
public class SurrealOplogStoreContractParityTests : OplogStoreContractTestBase
|
public class SurrealOplogStoreContractParityTests : OplogStoreContractTestBase
|
||||||
{
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
protected override Task<IOplogStoreContractHarness> CreateHarnessAsync()
|
protected override Task<IOplogStoreContractHarness> CreateHarnessAsync()
|
||||||
{
|
{
|
||||||
return Task.FromResult<IOplogStoreContractHarness>(new SurrealOplogStoreContractHarness());
|
return Task.FromResult<IOplogStoreContractHarness>(new SurrealOplogStoreContractHarness());
|
||||||
@@ -20,11 +21,15 @@ public class SurrealOplogStoreContractParityTests : OplogStoreContractTestBase
|
|||||||
|
|
||||||
public class LmdbOplogStoreContractTests : OplogStoreContractTestBase
|
public class LmdbOplogStoreContractTests : OplogStoreContractTestBase
|
||||||
{
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
protected override Task<IOplogStoreContractHarness> CreateHarnessAsync()
|
protected override Task<IOplogStoreContractHarness> CreateHarnessAsync()
|
||||||
{
|
{
|
||||||
return Task.FromResult<IOplogStoreContractHarness>(new LmdbOplogStoreContractHarness());
|
return Task.FromResult<IOplogStoreContractHarness>(new LmdbOplogStoreContractHarness());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies prune operations clear index tables as expected.
|
||||||
|
/// </summary>
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task Lmdb_IndexConsistency_InsertPopulatesAndPruneRemovesIndexes()
|
public async Task Lmdb_IndexConsistency_InsertPopulatesAndPruneRemovesIndexes()
|
||||||
{
|
{
|
||||||
@@ -54,6 +59,9 @@ public class LmdbOplogStoreContractTests : OplogStoreContractTestBase
|
|||||||
after.OplogNodeHeadCount.ShouldBe(0);
|
after.OplogNodeHeadCount.ShouldBe(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies prune retains newer entries while removing qualifying stale records.
|
||||||
|
/// </summary>
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task Lmdb_Prune_RemovesAtOrBeforeCutoff_AndKeepsNewerInterleavedEntries()
|
public async Task Lmdb_Prune_RemovesAtOrBeforeCutoff_AndKeepsNewerInterleavedEntries()
|
||||||
{
|
{
|
||||||
@@ -79,6 +87,9 @@ public class LmdbOplogStoreContractTests : OplogStoreContractTestBase
|
|||||||
remaining.Contains(nodeANew.Hash).ShouldBeTrue();
|
remaining.Contains(nodeANew.Hash).ShouldBeTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies node head values recompute correctly after prune operations.
|
||||||
|
/// </summary>
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task Lmdb_NodeHead_AdvancesAndRecomputesAcrossPrune()
|
public async Task Lmdb_NodeHead_AdvancesAndRecomputesAcrossPrune()
|
||||||
{
|
{
|
||||||
@@ -100,6 +111,9 @@ public class LmdbOplogStoreContractTests : OplogStoreContractTestBase
|
|||||||
(await store.GetLastEntryHashAsync("node-a")).ShouldBeNull();
|
(await store.GetLastEntryHashAsync("node-a")).ShouldBeNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies durable persistence preserves node head after reopen.
|
||||||
|
/// </summary>
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task Lmdb_RestartDurability_PreservesHeadAndScans()
|
public async Task Lmdb_RestartDurability_PreservesHeadAndScans()
|
||||||
{
|
{
|
||||||
@@ -121,6 +135,9 @@ public class LmdbOplogStoreContractTests : OplogStoreContractTestBase
|
|||||||
after[1].Hash.ShouldBe(entry2.Hash);
|
after[1].Hash.ShouldBe(entry2.Hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies appending duplicate entries remains idempotent.
|
||||||
|
/// </summary>
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task Lmdb_Dedupe_DuplicateHashAppendIsIdempotent()
|
public async Task Lmdb_Dedupe_DuplicateHashAppendIsIdempotent()
|
||||||
{
|
{
|
||||||
@@ -137,6 +154,9 @@ public class LmdbOplogStoreContractTests : OplogStoreContractTestBase
|
|||||||
exported[0].Hash.ShouldBe(entry.Hash);
|
exported[0].Hash.ShouldBe(entry.Hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies prune performance remains bounded under large synthetic datasets.
|
||||||
|
/// </summary>
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task Lmdb_PrunePerformanceSmoke_LargeSyntheticWindow_CompletesWithinGenerousBudget()
|
public async Task Lmdb_PrunePerformanceSmoke_LargeSyntheticWindow_CompletesWithinGenerousBudget()
|
||||||
{
|
{
|
||||||
@@ -164,30 +184,53 @@ internal sealed class SurrealOplogStoreContractHarness : IOplogStoreContractHarn
|
|||||||
{
|
{
|
||||||
private readonly SurrealTestHarness _harness;
|
private readonly SurrealTestHarness _harness;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new surrogate Surreal contract harness.
|
||||||
|
/// </summary>
|
||||||
public SurrealOplogStoreContractHarness()
|
public SurrealOplogStoreContractHarness()
|
||||||
{
|
{
|
||||||
_harness = new SurrealTestHarness();
|
_harness = new SurrealTestHarness();
|
||||||
Store = _harness.CreateOplogStore();
|
Store = _harness.CreateOplogStore();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the active store instance.
|
||||||
|
/// </summary>
|
||||||
public IOplogStore Store { get; private set; }
|
public IOplogStore Store { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reopens the Surreal store and returns a fresh harness handle.
|
||||||
|
/// </summary>
|
||||||
public IOplogStore ReopenStore()
|
public IOplogStore ReopenStore()
|
||||||
{
|
{
|
||||||
Store = _harness.CreateOplogStore();
|
Store = _harness.CreateOplogStore();
|
||||||
return Store;
|
return Store;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Appends an entry into the Surreal store for a dataset.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="entry">The oplog entry to append.</param>
|
||||||
|
/// <param name="datasetId">The dataset identifier for the append operation.</param>
|
||||||
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
public Task AppendOplogEntryAsync(OplogEntry entry, string datasetId, CancellationToken cancellationToken = default)
|
public Task AppendOplogEntryAsync(OplogEntry entry, string datasetId, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
return ((SurrealOplogStore)Store).AppendOplogEntryAsync(entry, datasetId, cancellationToken);
|
return ((SurrealOplogStore)Store).AppendOplogEntryAsync(entry, datasetId, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Exports all entries for a dataset from the Surreal store.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="datasetId">The dataset identifier to export.</param>
|
||||||
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
public Task<IEnumerable<OplogEntry>> ExportAsync(string datasetId, CancellationToken cancellationToken = default)
|
public Task<IEnumerable<OplogEntry>> ExportAsync(string datasetId, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
return ((SurrealOplogStore)Store).ExportAsync(datasetId, cancellationToken);
|
return ((SurrealOplogStore)Store).ExportAsync(datasetId, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Disposes Surreal harness resources.
|
||||||
|
/// </summary>
|
||||||
public ValueTask DisposeAsync()
|
public ValueTask DisposeAsync()
|
||||||
{
|
{
|
||||||
return _harness.DisposeAsync();
|
return _harness.DisposeAsync();
|
||||||
@@ -199,6 +242,9 @@ internal sealed class LmdbOplogStoreContractHarness : IOplogStoreContractHarness
|
|||||||
private readonly string _rootPath;
|
private readonly string _rootPath;
|
||||||
private LmdbOplogStore? _store;
|
private LmdbOplogStore? _store;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new LMDB contract harness and backing store.
|
||||||
|
/// </summary>
|
||||||
public LmdbOplogStoreContractHarness()
|
public LmdbOplogStoreContractHarness()
|
||||||
{
|
{
|
||||||
_rootPath = Path.Combine(Path.GetTempPath(), "cbddc-lmdb-tests", Guid.NewGuid().ToString("N"));
|
_rootPath = Path.Combine(Path.GetTempPath(), "cbddc-lmdb-tests", Guid.NewGuid().ToString("N"));
|
||||||
@@ -206,8 +252,14 @@ internal sealed class LmdbOplogStoreContractHarness : IOplogStoreContractHarness
|
|||||||
_store = CreateStore();
|
_store = CreateStore();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the active LMDB store.
|
||||||
|
/// </summary>
|
||||||
public IOplogStore Store => _store ?? throw new ObjectDisposedException(nameof(LmdbOplogStoreContractHarness));
|
public IOplogStore Store => _store ?? throw new ObjectDisposedException(nameof(LmdbOplogStoreContractHarness));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Recreates the LMDB store instance and returns it.
|
||||||
|
/// </summary>
|
||||||
public IOplogStore ReopenStore()
|
public IOplogStore ReopenStore()
|
||||||
{
|
{
|
||||||
_store?.Dispose();
|
_store?.Dispose();
|
||||||
@@ -215,18 +267,32 @@ internal sealed class LmdbOplogStoreContractHarness : IOplogStoreContractHarness
|
|||||||
return _store;
|
return _store;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Appends an entry into the LMDB store for a dataset.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="entry">The oplog entry to append.</param>
|
||||||
|
/// <param name="datasetId">The dataset identifier for the append operation.</param>
|
||||||
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
public Task AppendOplogEntryAsync(OplogEntry entry, string datasetId, CancellationToken cancellationToken = default)
|
public Task AppendOplogEntryAsync(OplogEntry entry, string datasetId, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
return (_store ?? throw new ObjectDisposedException(nameof(LmdbOplogStoreContractHarness)))
|
return (_store ?? throw new ObjectDisposedException(nameof(LmdbOplogStoreContractHarness)))
|
||||||
.AppendOplogEntryAsync(entry, datasetId, cancellationToken);
|
.AppendOplogEntryAsync(entry, datasetId, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Exports all entries for a dataset from the LMDB store.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="datasetId">The dataset identifier to export.</param>
|
||||||
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
public Task<IEnumerable<OplogEntry>> ExportAsync(string datasetId, CancellationToken cancellationToken = default)
|
public Task<IEnumerable<OplogEntry>> ExportAsync(string datasetId, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
return (_store ?? throw new ObjectDisposedException(nameof(LmdbOplogStoreContractHarness)))
|
return (_store ?? throw new ObjectDisposedException(nameof(LmdbOplogStoreContractHarness)))
|
||||||
.ExportAsync(datasetId, cancellationToken);
|
.ExportAsync(datasetId, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Disposes LMDB harness resources.
|
||||||
|
/// </summary>
|
||||||
public async ValueTask DisposeAsync()
|
public async ValueTask DisposeAsync()
|
||||||
{
|
{
|
||||||
_store?.Dispose();
|
_store?.Dispose();
|
||||||
|
|||||||
@@ -6,6 +6,9 @@ namespace ZB.MOM.WW.CBDDC.Sample.Console.Tests;
|
|||||||
|
|
||||||
public class MultiDatasetConfigParsingTests
|
public class MultiDatasetConfigParsingTests
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies multi-dataset section binds runtime options from JSON.
|
||||||
|
/// </summary>
|
||||||
[Fact]
|
[Fact]
|
||||||
public void MultiDatasetSection_ShouldBindRuntimeOptions()
|
public void MultiDatasetSection_ShouldBindRuntimeOptions()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -6,6 +6,9 @@ namespace ZB.MOM.WW.CBDDC.Sample.Console.Tests;
|
|||||||
|
|
||||||
public abstract class OplogStoreContractTestBase
|
public abstract class OplogStoreContractTestBase
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies append, merge, and drop behavior across query, chain, and restart scenarios.
|
||||||
|
/// </summary>
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task OplogStore_AppendQueryMergeDrop_AndLastHash_Works()
|
public async Task OplogStore_AppendQueryMergeDrop_AndLastHash_Works()
|
||||||
{
|
{
|
||||||
@@ -46,6 +49,9 @@ public abstract class OplogStoreContractTestBase
|
|||||||
(await rehydratedStore.ExportAsync()).ShouldBeEmpty();
|
(await rehydratedStore.ExportAsync()).ShouldBeEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies dataset isolation between primary and secondary stores.
|
||||||
|
/// </summary>
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task OplogStore_DatasetIsolation_Works()
|
public async Task OplogStore_DatasetIsolation_Works()
|
||||||
{
|
{
|
||||||
@@ -68,6 +74,9 @@ public abstract class OplogStoreContractTestBase
|
|||||||
logs[0].DatasetId.ShouldBe(DatasetId.Logs);
|
logs[0].DatasetId.ShouldBe(DatasetId.Logs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies chain range queries return ordered linked entries.
|
||||||
|
/// </summary>
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task OplogStore_GetChainRangeAsync_ReturnsOrderedLinkedRange()
|
public async Task OplogStore_GetChainRangeAsync_ReturnsOrderedLinkedRange()
|
||||||
{
|
{
|
||||||
@@ -89,8 +98,20 @@ public abstract class OplogStoreContractTestBase
|
|||||||
range[1].Hash.ShouldBe(entry3.Hash);
|
range[1].Hash.ShouldBe(entry3.Hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates the contract harness for this test class.
|
||||||
|
/// </summary>
|
||||||
protected abstract Task<IOplogStoreContractHarness> CreateHarnessAsync();
|
protected abstract Task<IOplogStoreContractHarness> CreateHarnessAsync();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a reusable oplog entry with deterministic timestamps.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="collection">The collection name.</param>
|
||||||
|
/// <param name="key">The entry key.</param>
|
||||||
|
/// <param name="nodeId">The node identifier generating the entry.</param>
|
||||||
|
/// <param name="wall">The wall-clock component of the HLC timestamp.</param>
|
||||||
|
/// <param name="logic">The logical clock component of the HLC timestamp.</param>
|
||||||
|
/// <param name="previousHash">The previous entry hash.</param>
|
||||||
protected static OplogEntry CreateOplogEntry(
|
protected static OplogEntry CreateOplogEntry(
|
||||||
string collection,
|
string collection,
|
||||||
string key,
|
string key,
|
||||||
@@ -111,11 +132,28 @@ public abstract class OplogStoreContractTestBase
|
|||||||
|
|
||||||
public interface IOplogStoreContractHarness : IAsyncDisposable
|
public interface IOplogStoreContractHarness : IAsyncDisposable
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the active contract store.
|
||||||
|
/// </summary>
|
||||||
IOplogStore Store { get; }
|
IOplogStore Store { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reopens the harness storage.
|
||||||
|
/// </summary>
|
||||||
IOplogStore ReopenStore();
|
IOplogStore ReopenStore();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Appends an entry for the specified dataset.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="entry">The oplog entry to append.</param>
|
||||||
|
/// <param name="datasetId">The dataset identifier.</param>
|
||||||
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
Task AppendOplogEntryAsync(OplogEntry entry, string datasetId, CancellationToken cancellationToken = default);
|
Task AppendOplogEntryAsync(OplogEntry entry, string datasetId, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Exports entries for the specified dataset.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="datasetId">The dataset identifier.</param>
|
||||||
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
Task<IEnumerable<OplogEntry>> ExportAsync(string datasetId, CancellationToken cancellationToken = default);
|
Task<IEnumerable<OplogEntry>> ExportAsync(string datasetId, CancellationToken cancellationToken = default);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using Microsoft.Extensions.Logging;
|
|||||||
using ZB.MOM.WW.CBDDC.Core.Network;
|
using ZB.MOM.WW.CBDDC.Core.Network;
|
||||||
using ZB.MOM.WW.CBDDC.Network;
|
using ZB.MOM.WW.CBDDC.Network;
|
||||||
using ZB.MOM.WW.CBDDC.Network.Security;
|
using ZB.MOM.WW.CBDDC.Network.Security;
|
||||||
|
using ZB.MOM.WW.CBDDC.Persistence.Lmdb;
|
||||||
using ZB.MOM.WW.CBDDC.Persistence.Surreal;
|
using ZB.MOM.WW.CBDDC.Persistence.Surreal;
|
||||||
using ZB.MOM.WW.CBDDC.Sample.Console;
|
using ZB.MOM.WW.CBDDC.Sample.Console;
|
||||||
|
|
||||||
@@ -15,6 +16,18 @@ internal sealed class BenchmarkPeerNode : IAsyncDisposable
|
|||||||
private readonly string _workDir;
|
private readonly string _workDir;
|
||||||
private bool _started;
|
private bool _started;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the active EF/Core context for generated users.
|
||||||
|
/// </summary>
|
||||||
|
public SampleDbContext Context { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates and initializes a benchmark peer node.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="serviceProvider">Service provider containing node dependencies.</param>
|
||||||
|
/// <param name="node">Benchmark node abstraction.</param>
|
||||||
|
/// <param name="context">Live data context used by the benchmark.</param>
|
||||||
|
/// <param name="workDir">Temporary working directory for this node.</param>
|
||||||
private BenchmarkPeerNode(
|
private BenchmarkPeerNode(
|
||||||
ServiceProvider serviceProvider,
|
ServiceProvider serviceProvider,
|
||||||
ICBDDCNode node,
|
ICBDDCNode node,
|
||||||
@@ -27,8 +40,13 @@ internal sealed class BenchmarkPeerNode : IAsyncDisposable
|
|||||||
_workDir = workDir;
|
_workDir = workDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SampleDbContext Context { get; }
|
/// <summary>
|
||||||
|
/// Creates and starts a benchmark peer node from configuration.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="nodeId">Unique peer identifier.</param>
|
||||||
|
/// <param name="tcpPort">Local TCP port for the node.</param>
|
||||||
|
/// <param name="authToken">Authentication token shared across peers.</param>
|
||||||
|
/// <param name="knownPeers">Known peers to connect to at startup.</param>
|
||||||
public static BenchmarkPeerNode Create(
|
public static BenchmarkPeerNode Create(
|
||||||
string nodeId,
|
string nodeId,
|
||||||
int tcpPort,
|
int tcpPort,
|
||||||
@@ -58,7 +76,13 @@ internal sealed class BenchmarkPeerNode : IAsyncDisposable
|
|||||||
services.AddSingleton<ICBDDCSurrealSchemaInitializer, SampleSurrealSchemaInitializer>();
|
services.AddSingleton<ICBDDCSurrealSchemaInitializer, SampleSurrealSchemaInitializer>();
|
||||||
services.AddSingleton<SampleDbContext>();
|
services.AddSingleton<SampleDbContext>();
|
||||||
|
|
||||||
services.AddCBDDCCore()
|
bool useLmdb = GetBoolEnv("CBDDC_BENCH_USE_LMDB", defaultValue: true);
|
||||||
|
bool dualWrite = GetBoolEnv("CBDDC_BENCH_DUAL_WRITE", defaultValue: true);
|
||||||
|
bool preferLmdbReads = GetBoolEnv("CBDDC_BENCH_PREFER_LMDB_READS", defaultValue: true);
|
||||||
|
bool enableShadowValidation = GetBoolEnv("CBDDC_BENCH_SHADOW_READ_VALIDATE", defaultValue: false);
|
||||||
|
int reconcileIntervalMs = GetIntEnv("CBDDC_BENCH_RECONCILE_INTERVAL_MS", defaultValue: 0);
|
||||||
|
|
||||||
|
var registration = services.AddCBDDCCore()
|
||||||
.AddCBDDCSurrealEmbedded<SampleDocumentStore>(_ => new CBDDCSurrealEmbeddedOptions
|
.AddCBDDCSurrealEmbedded<SampleDocumentStore>(_ => new CBDDCSurrealEmbeddedOptions
|
||||||
{
|
{
|
||||||
Endpoint = "rocksdb://local",
|
Endpoint = "rocksdb://local",
|
||||||
@@ -72,8 +96,27 @@ internal sealed class BenchmarkPeerNode : IAsyncDisposable
|
|||||||
PollingInterval = TimeSpan.FromMilliseconds(50),
|
PollingInterval = TimeSpan.FromMilliseconds(50),
|
||||||
EnableLiveSelectAccelerator = true
|
EnableLiveSelectAccelerator = true
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
.AddCBDDCNetwork<StaticPeerNodeConfigurationProvider>(false);
|
|
||||||
|
if (useLmdb)
|
||||||
|
registration.AddCBDDCLmdbOplog(
|
||||||
|
_ => new LmdbOplogOptions
|
||||||
|
{
|
||||||
|
EnvironmentPath = Path.Combine(workDir, "oplog-lmdb"),
|
||||||
|
MapSizeBytes = 256L * 1024 * 1024,
|
||||||
|
MaxDatabases = 16,
|
||||||
|
PruneBatchSize = 512
|
||||||
|
},
|
||||||
|
flags =>
|
||||||
|
{
|
||||||
|
flags.UseLmdbOplog = true;
|
||||||
|
flags.DualWriteOplog = dualWrite;
|
||||||
|
flags.PreferLmdbReads = preferLmdbReads;
|
||||||
|
flags.EnableReadShadowValidation = enableShadowValidation;
|
||||||
|
flags.ReconciliationInterval = TimeSpan.FromMilliseconds(Math.Max(0, reconcileIntervalMs));
|
||||||
|
});
|
||||||
|
|
||||||
|
registration.AddCBDDCNetwork<StaticPeerNodeConfigurationProvider>(false);
|
||||||
|
|
||||||
// Benchmark runs use explicit known peers; disable UDP discovery and handshake overhead.
|
// Benchmark runs use explicit known peers; disable UDP discovery and handshake overhead.
|
||||||
services.AddSingleton<IDiscoveryService, PassiveDiscoveryService>();
|
services.AddSingleton<IDiscoveryService, PassiveDiscoveryService>();
|
||||||
@@ -86,6 +129,9 @@ internal sealed class BenchmarkPeerNode : IAsyncDisposable
|
|||||||
return new BenchmarkPeerNode(provider, node, context, workDir);
|
return new BenchmarkPeerNode(provider, node, context, workDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Starts the node asynchronously.
|
||||||
|
/// </summary>
|
||||||
public async Task StartAsync()
|
public async Task StartAsync()
|
||||||
{
|
{
|
||||||
if (_started) return;
|
if (_started) return;
|
||||||
@@ -93,6 +139,9 @@ internal sealed class BenchmarkPeerNode : IAsyncDisposable
|
|||||||
_started = true;
|
_started = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Stops the node asynchronously.
|
||||||
|
/// </summary>
|
||||||
public async Task StopAsync()
|
public async Task StopAsync()
|
||||||
{
|
{
|
||||||
if (!_started) return;
|
if (!_started) return;
|
||||||
@@ -111,22 +160,37 @@ internal sealed class BenchmarkPeerNode : IAsyncDisposable
|
|||||||
_started = false;
|
_started = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Inserts or updates a user record.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="user">User payload.</param>
|
||||||
public async Task UpsertUserAsync(User user)
|
public async Task UpsertUserAsync(User user)
|
||||||
{
|
{
|
||||||
await Context.Users.UpdateAsync(user);
|
await Context.Users.UpdateAsync(user);
|
||||||
await Context.SaveChangesAsync();
|
await Context.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns whether a user identifier exists.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="userId">Target user identifier.</param>
|
||||||
public bool ContainsUser(string userId)
|
public bool ContainsUser(string userId)
|
||||||
{
|
{
|
||||||
return Context.Users.Find(u => u.Id == userId).Any();
|
return Context.Users.Find(u => u.Id == userId).Any();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Counts user identifiers matching the provided prefix.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="prefix">User identifier prefix.</param>
|
||||||
public int CountUsersWithPrefix(string prefix)
|
public int CountUsersWithPrefix(string prefix)
|
||||||
{
|
{
|
||||||
return Context.Users.FindAll().Count(u => u.Id.StartsWith(prefix, StringComparison.Ordinal));
|
return Context.Users.FindAll().Count(u => u.Id.StartsWith(prefix, StringComparison.Ordinal));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Disposes the node and any unmanaged resources.
|
||||||
|
/// </summary>
|
||||||
public async ValueTask DisposeAsync()
|
public async ValueTask DisposeAsync()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -156,18 +220,43 @@ internal sealed class BenchmarkPeerNode : IAsyncDisposable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool GetBoolEnv(string key, bool defaultValue)
|
||||||
|
{
|
||||||
|
string? raw = Environment.GetEnvironmentVariable(key);
|
||||||
|
if (string.IsNullOrWhiteSpace(raw)) return defaultValue;
|
||||||
|
if (bool.TryParse(raw, out bool parsed)) return parsed;
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int GetIntEnv(string key, int defaultValue)
|
||||||
|
{
|
||||||
|
string? raw = Environment.GetEnvironmentVariable(key);
|
||||||
|
if (string.IsNullOrWhiteSpace(raw)) return defaultValue;
|
||||||
|
if (int.TryParse(raw, out int parsed)) return parsed;
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
private sealed class PassiveDiscoveryService : IDiscoveryService
|
private sealed class PassiveDiscoveryService : IDiscoveryService
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the current list of active peers.
|
||||||
|
/// </summary>
|
||||||
public IEnumerable<PeerNode> GetActivePeers()
|
public IEnumerable<PeerNode> GetActivePeers()
|
||||||
{
|
{
|
||||||
return Array.Empty<PeerNode>();
|
return Array.Empty<PeerNode>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Starts discovery.
|
||||||
|
/// </summary>
|
||||||
public Task Start()
|
public Task Start()
|
||||||
{
|
{
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Stops discovery.
|
||||||
|
/// </summary>
|
||||||
public Task Stop()
|
public Task Stop()
|
||||||
{
|
{
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ public class E2EThroughputBenchmarks
|
|||||||
private BenchmarkPeerNode _nodeB = null!;
|
private BenchmarkPeerNode _nodeB = null!;
|
||||||
private int _sequence;
|
private int _sequence;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets up benchmark nodes and prepares the cluster.
|
||||||
|
/// </summary>
|
||||||
[GlobalSetup]
|
[GlobalSetup]
|
||||||
public async Task GlobalSetupAsync()
|
public async Task GlobalSetupAsync()
|
||||||
{
|
{
|
||||||
@@ -58,6 +61,9 @@ public class E2EThroughputBenchmarks
|
|||||||
await Task.Delay(500);
|
await Task.Delay(500);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles benchmark teardown for the throughput test suite.
|
||||||
|
/// </summary>
|
||||||
[GlobalCleanup]
|
[GlobalCleanup]
|
||||||
public Task GlobalCleanupAsync()
|
public Task GlobalCleanupAsync()
|
||||||
{
|
{
|
||||||
@@ -66,6 +72,9 @@ public class E2EThroughputBenchmarks
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Measures local write throughput against a single node.
|
||||||
|
/// </summary>
|
||||||
[Benchmark(Description = "Local write throughput", OperationsPerInvoke = BatchSize)]
|
[Benchmark(Description = "Local write throughput", OperationsPerInvoke = BatchSize)]
|
||||||
public async Task LocalWriteThroughput()
|
public async Task LocalWriteThroughput()
|
||||||
{
|
{
|
||||||
@@ -74,6 +83,9 @@ public class E2EThroughputBenchmarks
|
|||||||
await _nodeA.UpsertUserAsync(CreateUser(userId));
|
await _nodeA.UpsertUserAsync(CreateUser(userId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Measures replicated write throughput across two nodes.
|
||||||
|
/// </summary>
|
||||||
[Benchmark(Description = "Cross-node replicated throughput", OperationsPerInvoke = BatchSize)]
|
[Benchmark(Description = "Cross-node replicated throughput", OperationsPerInvoke = BatchSize)]
|
||||||
public async Task ReplicatedWriteThroughput()
|
public async Task ReplicatedWriteThroughput()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -10,18 +10,24 @@ namespace ZB.MOM.WW.CDBBC.E2E.Benchmark.Tests;
|
|||||||
[SimpleJob(launchCount: 1, warmupCount: 0, iterationCount: 1)]
|
[SimpleJob(launchCount: 1, warmupCount: 0, iterationCount: 1)]
|
||||||
public class OfflineResyncThroughputBenchmarks
|
public class OfflineResyncThroughputBenchmarks
|
||||||
{
|
{
|
||||||
private const int BacklogOperationCount = 10_000;
|
private const int BacklogOperationCount = 100_000;
|
||||||
private BenchmarkPeerNode _onlineNode = null!;
|
private BenchmarkPeerNode _onlineNode = null!;
|
||||||
private BenchmarkPeerNode _offlineNode = null!;
|
private BenchmarkPeerNode _offlineNode = null!;
|
||||||
private int _runSequence;
|
private int _runSequence;
|
||||||
private string _currentPrefix = string.Empty;
|
private string _currentPrefix = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets up benchmark resources for offline resync scenarios.
|
||||||
|
/// </summary>
|
||||||
[GlobalSetup]
|
[GlobalSetup]
|
||||||
public Task GlobalSetupAsync()
|
public Task GlobalSetupAsync()
|
||||||
{
|
{
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles benchmark teardown for offline resync scenarios.
|
||||||
|
/// </summary>
|
||||||
[GlobalCleanup]
|
[GlobalCleanup]
|
||||||
public Task GlobalCleanupAsync()
|
public Task GlobalCleanupAsync()
|
||||||
{
|
{
|
||||||
@@ -30,6 +36,9 @@ public class OfflineResyncThroughputBenchmarks
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Prepares write-only workload state for the 100K throughput benchmark.
|
||||||
|
/// </summary>
|
||||||
[IterationSetup(Target = nameof(OfflineBacklogWriteThroughput100k))]
|
[IterationSetup(Target = nameof(OfflineBacklogWriteThroughput100k))]
|
||||||
public void SetupOfflineWriteThroughput()
|
public void SetupOfflineWriteThroughput()
|
||||||
{
|
{
|
||||||
@@ -37,12 +46,18 @@ public class OfflineResyncThroughputBenchmarks
|
|||||||
InitializeIterationNodesAsync().GetAwaiter().GetResult();
|
InitializeIterationNodesAsync().GetAwaiter().GetResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Benchmark(Description = "Offline backlog write throughput (10K ops)", OperationsPerInvoke = BacklogOperationCount)]
|
/// <summary>
|
||||||
|
/// Measures offline backlog write throughput for 100K operations.
|
||||||
|
/// </summary>
|
||||||
|
[Benchmark(Description = "Offline backlog write throughput (100K ops)", OperationsPerInvoke = BacklogOperationCount)]
|
||||||
public async Task OfflineBacklogWriteThroughput100k()
|
public async Task OfflineBacklogWriteThroughput100k()
|
||||||
{
|
{
|
||||||
await WriteBatchAsync(_currentPrefix, BacklogOperationCount);
|
await WriteBatchAsync(_currentPrefix, BacklogOperationCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Prepares nodes and backlog before the re-sync benchmark iteration.
|
||||||
|
/// </summary>
|
||||||
[IterationSetup(Target = nameof(OfflineNodeResyncDurationAfter100kBacklog))]
|
[IterationSetup(Target = nameof(OfflineNodeResyncDurationAfter100kBacklog))]
|
||||||
public void SetupOfflineResyncBenchmark()
|
public void SetupOfflineResyncBenchmark()
|
||||||
{
|
{
|
||||||
@@ -51,7 +66,10 @@ public class OfflineResyncThroughputBenchmarks
|
|||||||
WriteBatchAsync(_currentPrefix, BacklogOperationCount).GetAwaiter().GetResult();
|
WriteBatchAsync(_currentPrefix, BacklogOperationCount).GetAwaiter().GetResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Benchmark(Description = "Offline node re-sync duration after 10K backlog")]
|
/// <summary>
|
||||||
|
/// Measures re-sync duration after processing a 100K-entry offline backlog.
|
||||||
|
/// </summary>
|
||||||
|
[Benchmark(Description = "Offline node re-sync duration after 100K backlog")]
|
||||||
public async Task OfflineNodeResyncDurationAfter100kBacklog()
|
public async Task OfflineNodeResyncDurationAfter100kBacklog()
|
||||||
{
|
{
|
||||||
await _offlineNode.StartAsync();
|
await _offlineNode.StartAsync();
|
||||||
|
|||||||
@@ -57,6 +57,9 @@ public class SurrealLogStorageBenchmarks
|
|||||||
private string _contextNumericKeyQueryValue = string.Empty;
|
private string _contextNumericKeyQueryValue = string.Empty;
|
||||||
private int _contextNumericValueQueryValue;
|
private int _contextNumericValueQueryValue;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes the benchmark environment and seeds the dataset.
|
||||||
|
/// </summary>
|
||||||
[GlobalSetup]
|
[GlobalSetup]
|
||||||
public async Task GlobalSetupAsync()
|
public async Task GlobalSetupAsync()
|
||||||
{
|
{
|
||||||
@@ -97,6 +100,9 @@ public class SurrealLogStorageBenchmarks
|
|||||||
$"RocksDB size: {sizeBytes / (1024d * 1024d):F2} MiB ({sizeBytes:N0} bytes). Path: {_databasePath}");
|
$"RocksDB size: {sizeBytes / (1024d * 1024d):F2} MiB ({sizeBytes:N0} bytes). Path: {_databasePath}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles benchmark teardown for surreal log storage scenarios.
|
||||||
|
/// </summary>
|
||||||
[GlobalCleanup]
|
[GlobalCleanup]
|
||||||
public Task GlobalCleanupAsync()
|
public Task GlobalCleanupAsync()
|
||||||
{
|
{
|
||||||
@@ -104,6 +110,9 @@ public class SurrealLogStorageBenchmarks
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Queries the latest log rows by context identifier.
|
||||||
|
/// </summary>
|
||||||
[Benchmark(Description = "Query by contextId (latest 200 rows)")]
|
[Benchmark(Description = "Query by contextId (latest 200 rows)")]
|
||||||
public async Task QueryByContextIdAsync()
|
public async Task QueryByContextIdAsync()
|
||||||
{
|
{
|
||||||
@@ -117,6 +126,9 @@ public class SurrealLogStorageBenchmarks
|
|||||||
new Dictionary<string, object?> { ["contextId"] = _contextIdQueryValue });
|
new Dictionary<string, object?> { ["contextId"] = _contextIdQueryValue });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Queries the latest rows by logger and timestamp range.
|
||||||
|
/// </summary>
|
||||||
[Benchmark(Description = "Query by loggerName + timestamp range (latest 200 rows)")]
|
[Benchmark(Description = "Query by loggerName + timestamp range (latest 200 rows)")]
|
||||||
public async Task QueryByLoggerAndTimestampAsync()
|
public async Task QueryByLoggerAndTimestampAsync()
|
||||||
{
|
{
|
||||||
@@ -137,6 +149,9 @@ public class SurrealLogStorageBenchmarks
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Queries rows by logger and context key/value pairs.
|
||||||
|
/// </summary>
|
||||||
[Benchmark(Description = "Query by loggerName + timestamp + arbitrary context string key/value")]
|
[Benchmark(Description = "Query by loggerName + timestamp + arbitrary context string key/value")]
|
||||||
public async Task QueryByLoggerTimestampAndContextKeyAsync()
|
public async Task QueryByLoggerTimestampAndContextKeyAsync()
|
||||||
{
|
{
|
||||||
@@ -169,6 +184,9 @@ public class SurrealLogStorageBenchmarks
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Queries rows by logger and numeric context key/value pairs.
|
||||||
|
/// </summary>
|
||||||
[Benchmark(Description = "Query by loggerName + timestamp + arbitrary context number key/value")]
|
[Benchmark(Description = "Query by loggerName + timestamp + arbitrary context number key/value")]
|
||||||
public async Task QueryByLoggerTimestampAndNumericContextKeyAsync()
|
public async Task QueryByLoggerTimestampAndNumericContextKeyAsync()
|
||||||
{
|
{
|
||||||
@@ -201,6 +219,9 @@ public class SurrealLogStorageBenchmarks
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reports RocksDB size for the seeded benchmark database.
|
||||||
|
/// </summary>
|
||||||
[Benchmark(Description = "RocksDB size (bytes)")]
|
[Benchmark(Description = "RocksDB size (bytes)")]
|
||||||
public long GetDatabaseFileSizeBytes()
|
public long GetDatabaseFileSizeBytes()
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user