Repository navigation
Expand file tree
/
Copy pathAtomicMutationApplication.cs
More file actions
143 lines (135 loc) · 9.87 KB
/
Copy pathAtomicMutationApplication.cs
File metadata and controls
143 lines (135 loc) · 9.87 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
using System.Collections.Immutable;
using KeyLoad.Core.DocumentStorage;
using KeyLoad.Core.Messaging;
using KeyLoad.Storage;
using ManagedCode.Communication.Commands;
namespace KeyLoad.Core;
/// <summary>Executes AtomicMutationApplication operations against the caller's database context.</summary>
public static class AtomicMutationApplication
{
internal const int AtomicMutationApplicationAdjacentElementOffset = 1;
internal const string UnsupportedMutationMessage = "The mutation is unsupported.";
internal const string DocumentEpochSpace = "document-epoch";
internal static ImmutableArray<MutationReceipt> ApplyMutations(this DatabaseEngine engine, IAtomicTransaction tx, PrincipalRecord principal, PartitionRef partition, ImmutableArray<Mutation> mutations, DateTimeOffset now, long position, CommitToken? commitToken = null, Command<DatabaseCommand>? operation = null, bool preparingComposition = false, ReadExecutionBudget? compositionBudget = null)
{
var token = commitToken ?? engine.Token(tx, partition, position);
var receipts = ImmutableArray.CreateBuilder<MutationReceipt>(mutations.Length);
var originalPayload = preparingComposition ? operation!.Value! : null;
List<QueueRetryDecision>? previewChoices = preparingComposition ? [] : null;
ResourceDefinition? definition = null;
ResourceLifetime? lifetime = null;
OutboxHead? outboxHead = null;
foreach (var (mutation, derived) in engine.ExpandCommandMutations(tx, principal, partition, mutations, now, compositionBudget))
{
var documentId = mutation switch
{
PutDocument put => put.Id,
PatchDocument patch => patch.Id,
DeleteDocument delete => delete.Id,
_ => null
};
var key = documentId is null ? null : DocumentStorageExecutionDocuments.DocumentKey(partition, mutation.Resource, documentId);
var before = key is null ? null : tx.GetRecord<DocumentRecord>(key);
var context = new DocumentMutationContext(key!, before);
if (mutation is not (PutDocument or PatchDocument or DeleteDocument))
{
definition = null;
}
else if (definition?.Name != mutation.Resource)
{
(definition, lifetime) = ResourceDefinitionBinding.ReadBoundResourcePair(tx, partition, mutation.Resource, ResourceKind.Collection);
}
DocumentMutationResult? image = mutation switch
{
PutDocument put => engine.Put(tx, principal, partition, put, now, context, definition!, lifetime!),
PatchDocument patch => engine.Patch(tx, principal, patch, now, context, definition!, lifetime!),
DeleteDocument delete => engine.Delete(tx, principal, delete, now, context, definition!, lifetime!),
_ => null
};
var effectOperation = preparingComposition ? engine.PrepareCompositionQueueRetry(tx, operation!, partition, mutation, originalPayload!, previewChoices!) : operation;
var receipt = image?.Receipt ?? engine.ApplyNonDocumentMutation(tx, principal, partition, mutation, now, position, effectOperation);
if (derived)
{
receipt = receipt with
{
CompositionReferences = DatabaseCompositionIdentity.CompositionMutationReferences(mutation)
};
}
receipts.Add(receipt);
var after = image?.After;
ProjectionOutbox.AppendOutbox(tx, partition, receipts.Count - AtomicMutationApplicationAdjacentElementOffset,
token, now, mutation, receipt, before, after, ref outboxHead);
if (before is not null && after is not null && before.Access != after.Access)
{
ChangeFeedsExecutionChangeFeeds.AdvanceVisibilityEpoch(tx, partition, mutation.Resource);
}
}
foreach (var collection in mutations.Where(mutation => mutation is InsertTypedRow or PutDocument or PatchDocument or DeleteDocument).Select(mutation => mutation.Resource).Distinct(StringComparer.Ordinal))
{
tx.PutRecord(KeySpace.Partition(DocumentEpochSpace, partition, collection), checked(engine.DocumentEpoch(tx, partition, collection) + AtomicMutationApplicationAdjacentElementOffset));
}
ResourceEffectCompositionScopes.RequireCompleteCompositionEffects(tx, operation, preparingComposition);
return receipts.Count == receipts.Capacity ? receipts.MoveToImmutable() : receipts.ToImmutable();
}
internal static IEnumerable<(Mutation Effect, bool Derived)> ExpandCommandMutations(this DatabaseEngine engine, IAtomicTransaction tx, PrincipalRecord principal, PartitionRef partition, ImmutableArray<Mutation> mutations, DateTimeOffset now, ReadExecutionBudget? compositionBudget = null)
{
var budget = compositionBudget;
foreach (var requested in mutations)
{
if (requested is InsertTypedRow insert)
{
yield return (engine.ExpandTypedRowInsert(tx, principal, partition, insert), false);
continue;
}
if (requested is not (QueueToGraph or GraphToQueueMutation))
{
yield return (requested, false);
continue;
}
budget ??= new ReadExecutionBudget();
foreach (var effect in engine.ExpandComposition(tx, principal, partition, requested, now, budget))
{
yield return (effect, true);
}
}
}
internal static MutationReceipt ApplyNonDocumentMutation(this DatabaseEngine engine, IAtomicTransaction tx, PrincipalRecord principal, PartitionRef partition, Mutation mutation, DateTimeOffset now, long position, Command<DatabaseCommand>? operation) => mutation switch
{
UpsertAnalyticsEvents analytics => engine.ApplyAnalyticsUpsert(tx, principal, partition, analytics, now),
DeleteAnalyticsEvents analytics => engine.ApplyAnalyticsDelete(tx, principal, partition, analytics, now),
AppendEvents events => engine.Append(tx, principal, partition, events, now),
PublishTopic topic => engine.Publish(tx, principal, partition, topic, now),
PurgeTopic topic => engine.PurgeTopicHistory(tx, partition, topic),
EnqueueMessage message => engine.Enqueue(tx, principal, partition, message, now),
AdvanceQueueDeadline message => engine.ApplyQueueDeadline(tx, partition, message, now, operation),
RedriveQueueMessage message => engine.ApplyRedriveQueueMessage(tx, partition, message, now),
CancelQueueMessage message => engine.ApplyCancelQueueMessage(tx, partition, message),
ParkPendingQueueMessage message => engine.ApplyParkPendingQueueMessage(tx, partition, message, now),
UpsertEdge edge => engine.Upsert(tx, principal, partition, edge),
DeleteEdge edge => engine.RemoveEdge(tx, principal, partition, edge),
ApplyCrossPartitionReverseEdge edge => engine.ApplyGraphReverseDelivery(tx, principal, partition, edge),
CompleteCrossPartitionReverseEdge edge => engine.CompleteGraphReverseDelivery(tx, principal, partition, edge),
AppendSamples samples => engine.Append(tx, principal, partition, samples, now),
OpenSampleChunkWindow window => global::KeyLoad.Core.TimeSeries.SampleChunkWindowOpen.Execute(engine, tx, principal, partition, window, engine.TimeSeriesOptions),
SealSampleChunkWindow window => global::KeyLoad.Core.TimeSeries.SampleChunkWindowSeal.Execute(engine, tx, principal, partition, window, engine.TimeSeriesOptions),
MergeSampleChunkWindow window => global::KeyLoad.Core.TimeSeries.SampleChunkWindowMerge.Execute(engine, tx, principal, partition, window, engine.TimeSeriesOptions),
DropSampleChunkWindow window => global::KeyLoad.Core.TimeSeries.SampleChunkWindowDrop.Execute(engine, tx, principal, partition, window, engine.TimeSeriesOptions),
ExpireSamples samples => engine.Expire(tx, principal, partition, samples, now),
RefreshSampleRollup rollup => global::KeyLoad.Core.TimeSeries.SampleRollupCommands.Refresh(engine, tx, principal, partition, rollup, engine.TimeSeriesExecution.MaximumRollupBuckets),
DropSampleRollup rollup => global::KeyLoad.Core.TimeSeries.SampleRollupCommands.Drop(engine, tx, principal, partition, rollup),
StoreAggregateSnapshot snapshot => engine.SaveAggregateSnapshot(tx, principal, partition, snapshot),
PutVector vector => engine.Upsert(tx, principal, partition, vector),
CreateQueueTransfer transfer => engine.ApplyCreateQueueTransfer(tx, principal, partition, transfer, now),
AcceptQueueTransfer transfer => engine.ApplyAcceptQueueTransfer(tx, principal, partition, transfer, now, position),
CompleteQueueTransfer transfer => engine.ApplyCompleteQueueTransfer(tx, principal, partition, transfer),
AdvanceQueueTransferAttempt transfer => engine.ApplyAdvanceQueueTransferAttempt(tx, principal, partition, transfer),
AdvanceQueueTransferRepair transfer => engine.ApplyAdvanceQueueTransferRepair(tx, principal, partition, transfer),
ApplyVectorProjection projection => engine.ApplyVectorProjection(tx, principal, partition, projection),
ConfigureRecurringSchedule schedule => engine.ApplyConfigureRecurringSchedule(tx, principal, partition, schedule, now),
EmitRecurringOccurrences schedule => engine.ApplyEmitRecurringOccurrences(tx, principal, partition, schedule, now),
CancelRecurringSchedule schedule => engine.ApplyCancelRecurringSchedule(tx, principal, partition, schedule, now),
CompareExchangeSaga saga => engine.ApplyCompareExchangeSaga(tx, principal, partition, saga, now),
ExpireSaga saga => engine.ApplyExpireSaga(tx, principal, partition, saga, now),
_ => throw Errors.Fail(ErrorCode.UnsupportedCapability, UnsupportedMutationMessage)
};
}