Delete CultNetDatabase's transaction wrapper
ExecuteTransactionAsync (both overloads), RequireTransactionsForAuthoritativeWrites, EnsureAuthoritativeTransaction and the AfterCommit deferral had no consumer outside CultNetDatabase_RequiredTransaction_DefersRawPutPublicationUntilCommit, deleted with them. Writes publish immediately, as they already did outside a transaction. The cache's own transaction machinery stays until Cut 3 replaces it with the explicit batch commit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
d26f357f3e
commit
88e1c8e157
2 changed files with 5 additions and 140 deletions
|
|
@ -298,11 +298,6 @@ namespace GameCult.Networking
|
|||
/// Gets or sets the durable store for accepted shard mutation logs.
|
||||
/// </summary>
|
||||
public ICultNetShardMutationLogStore? MutationLogStore { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets whether authoritative writes must occur inside ExecuteTransactionAsync.
|
||||
/// Use this for state owners that forbid record-at-a-time mutation bypasses.
|
||||
/// </summary>
|
||||
public bool RequireTransactionsForAuthoritativeWrites { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -470,8 +465,6 @@ namespace GameCult.Networking
|
|||
private readonly Dictionary<string, long> _nextLogSequences = new(StringComparer.Ordinal);
|
||||
private readonly Dictionary<string, long> _appliedShardSequences = new(StringComparer.Ordinal);
|
||||
private readonly Subject<object> _changes = new();
|
||||
private readonly AsyncLocal<CultNetDatabaseTransaction?> _ambientTransaction = new();
|
||||
private readonly bool _requireTransactionsForAuthoritativeWrites;
|
||||
private bool _disposed;
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -484,7 +477,6 @@ namespace GameCult.Networking
|
|||
_runtimeId = options.RuntimeId;
|
||||
_documents = options.DocumentRegistry ?? new CultNetDocumentRegistry(cache.Registry);
|
||||
_mutationLogStore = options.MutationLogStore;
|
||||
_requireTransactionsForAuthoritativeWrites = options.RequireTransactionsForAuthoritativeWrites;
|
||||
_shards = (options.Shards == null || options.Shards.Count == 0
|
||||
? new[] { CultNetShardDescriptor.PrimaryAll(options.RuntimeId) }
|
||||
: options.Shards)
|
||||
|
|
@ -512,49 +504,6 @@ namespace GameCult.Networking
|
|||
/// </summary>
|
||||
public IReadOnlyList<CultNetClientAuthorityScope> ClientAuthorityScopes => _clientAuthorityScopes;
|
||||
|
||||
/// <summary>
|
||||
/// Runs one buffered authoritative database mutation. Cache state, durable state,
|
||||
/// mutation logs, and database observers advance only after the complete stage succeeds.
|
||||
/// </summary>
|
||||
public async Task ExecuteTransactionAsync(Func<Task> stageAsync, bool soft = false)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
if (stageAsync == null) throw new ArgumentNullException(nameof(stageAsync));
|
||||
if (_ambientTransaction.Value != null)
|
||||
{
|
||||
await stageAsync().ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
var transaction = new CultNetDatabaseTransaction();
|
||||
IReadOnlyList<Action> afterCommit;
|
||||
_ambientTransaction.Value = transaction;
|
||||
try
|
||||
{
|
||||
await _cache.ExecuteTransactionAsync(stageAsync, soft).ConfigureAwait(false);
|
||||
afterCommit = transaction.AfterCommit.ToArray();
|
||||
}
|
||||
finally
|
||||
{
|
||||
_ambientTransaction.Value = null;
|
||||
}
|
||||
|
||||
foreach (var publish in afterCommit)
|
||||
publish();
|
||||
}
|
||||
|
||||
/// <summary>Runs one value-producing buffered authoritative database mutation.</summary>
|
||||
public async Task<T> ExecuteTransactionAsync<T>(Func<Task<T>> stageAsync, bool soft = false)
|
||||
{
|
||||
if (stageAsync == null) throw new ArgumentNullException(nameof(stageAsync));
|
||||
T result = default!;
|
||||
await ExecuteTransactionAsync(async () =>
|
||||
{
|
||||
result = await stageAsync().ConfigureAwait(false);
|
||||
}, soft).ConfigureAwait(false);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>Gets whether this database instance can authoritatively replace the document at a key.</summary>
|
||||
public bool CanWriteAuthoritatively<T>(CultRecordKey key) where T : class
|
||||
{
|
||||
|
|
@ -854,7 +803,6 @@ namespace GameCult.Networking
|
|||
public async Task<CultRecordHandle<T>> PutAsync<T>(CultRecordKey key, T document) where T : class
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
EnsureAuthoritativeTransaction();
|
||||
if (document == null) throw new ArgumentNullException(nameof(document));
|
||||
|
||||
var descriptor = _cache.Registry.GetRequired<T>();
|
||||
|
|
@ -880,10 +828,7 @@ namespace GameCult.Networking
|
|||
document,
|
||||
previous));
|
||||
}
|
||||
if (_ambientTransaction.Value is { } transaction)
|
||||
transaction.AfterCommit.Add(PublishCommittedPut);
|
||||
else
|
||||
PublishCommittedPut();
|
||||
PublishCommittedPut();
|
||||
return handle;
|
||||
}
|
||||
|
||||
|
|
@ -918,7 +863,6 @@ namespace GameCult.Networking
|
|||
public Task DeleteAsync<T>(CultRecordKey key) where T : class
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
EnsureAuthoritativeTransaction();
|
||||
var descriptor = _cache.Registry.GetRequired<T>();
|
||||
var shard = ResolveShardInternal(descriptor, key);
|
||||
EnsurePrimary(shard, descriptor.SchemaId, key);
|
||||
|
|
@ -944,10 +888,7 @@ namespace GameCult.Networking
|
|||
document: null,
|
||||
previousDocument: previous));
|
||||
}
|
||||
if (_ambientTransaction.Value is { } transaction)
|
||||
transaction.AfterCommit.Add(PublishCommittedDelete);
|
||||
else
|
||||
PublishCommittedDelete();
|
||||
PublishCommittedDelete();
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
|
|
@ -959,7 +900,6 @@ namespace GameCult.Networking
|
|||
public async Task<object> ApplyPutAsync(CultNetDocumentPutRawMessage message)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
EnsureAuthoritativeTransaction();
|
||||
if (message == null) throw new ArgumentNullException(nameof(message));
|
||||
if (message.Document == null)
|
||||
{
|
||||
|
|
@ -1005,10 +945,7 @@ namespace GameCult.Networking
|
|||
document,
|
||||
previous);
|
||||
}
|
||||
if (_ambientTransaction.Value is { } transaction)
|
||||
transaction.AfterCommit.Add(PublishCommittedPut);
|
||||
else
|
||||
PublishCommittedPut();
|
||||
PublishCommittedPut();
|
||||
return document;
|
||||
}
|
||||
|
||||
|
|
@ -1018,7 +955,6 @@ namespace GameCult.Networking
|
|||
public Task ApplyDeleteAsync(CultNetDocumentDeleteMessage message)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
EnsureAuthoritativeTransaction();
|
||||
if (message == null) throw new ArgumentNullException(nameof(message));
|
||||
|
||||
var key = new CultRecordKey(message.RecordKey);
|
||||
|
|
@ -1055,10 +991,7 @@ namespace GameCult.Networking
|
|||
document: null,
|
||||
previousDocument: previous);
|
||||
}
|
||||
if (_ambientTransaction.Value is { } transaction)
|
||||
transaction.AfterCommit.Add(PublishCommittedDelete);
|
||||
else
|
||||
PublishCommittedDelete();
|
||||
PublishCommittedDelete();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
|
|
@ -1771,20 +1704,6 @@ namespace GameCult.Networking
|
|||
};
|
||||
}
|
||||
|
||||
private sealed class CultNetDatabaseTransaction
|
||||
{
|
||||
public List<Action> AfterCommit { get; } = new();
|
||||
}
|
||||
|
||||
private void EnsureAuthoritativeTransaction()
|
||||
{
|
||||
if (_requireTransactionsForAuthoritativeWrites && _ambientTransaction.Value == null)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"This CultNet database requires authoritative writes to use ExecuteTransactionAsync.");
|
||||
}
|
||||
}
|
||||
|
||||
private void ThrowIfDisposed()
|
||||
{
|
||||
if (_disposed)
|
||||
|
|
|
|||
Loading…
Reference in a new issue