-
Notifications
You must be signed in to change notification settings - Fork 1k
Expand file tree
/
Copy pathProcedureContext.cs
More file actions
139 lines (112 loc) · 4.64 KB
/
ProcedureContext.cs
File metadata and controls
139 lines (112 loc) · 4.64 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
namespace SpacetimeDB;
#pragma warning disable STDB_UNSTABLE
public abstract class ProcedureContextBase : Internal.IInternalProcedureContext
{
public static Identity Identity => Internal.IProcedureContext.GetIdentity();
public Identity Sender { get; }
public ConnectionId? ConnectionId { get; }
public Random Rng => txState.Rng;
public Timestamp Timestamp => txState.Timestamp;
public AuthCtx SenderAuth { get; }
// NOTE: The host rejects procedure HTTP requests while a mut transaction is open
// (WOULD_BLOCK_TRANSACTION). Avoid calling `Http.*` inside WithTx.
public HttpClient Http { get; } = new();
// **Note:** must be 0..=u32::MAX
protected int CounterUuid = 0;
private readonly TransactionalContextState<ProcedureTxContextBase> txState;
protected ProcedureContextBase(
Identity sender,
ConnectionId? connectionId,
Random random,
Timestamp time
)
{
Sender = sender;
ConnectionId = connectionId;
SenderAuth = AuthCtx.BuildFromSystemTables(connectionId, sender);
txState = new(
random,
time,
timestamp => new Internal.TxContext(
CreateLocal(),
Sender,
ConnectionId,
timestamp,
SenderAuth,
random
),
inner => CreateTxContext(inner)
);
}
protected abstract ProcedureTxContextBase CreateTxContext(Internal.TxContext inner);
protected internal abstract LocalBase CreateLocal();
public Internal.TxContext EnterTxContext(long timestampMicros) =>
txState.EnterTxContext(timestampMicros);
public void ExitTxContext() => txState.ExitTxContext();
public readonly struct TxOutcome<TResult>(bool isSuccess, TResult? value, Exception? error)
{
public bool IsSuccess { get; } = isSuccess;
public TResult? Value { get; } = value;
public Exception? Error { get; } = error;
public static TxOutcome<TResult> Success(TResult value) => new(true, value, null);
public static TxOutcome<TResult> Failure(Exception error) => new(false, default, error);
public TResult UnwrapOrThrow() =>
IsSuccess
? Value!
: throw (
Error
?? new InvalidOperationException("Transaction failed without an error object.")
);
public TResult UnwrapOrThrow(Func<Exception> fallbackFactory) =>
IsSuccess ? Value! : throw (Error ?? fallbackFactory());
}
public TResult WithTx<TResult>(Func<ProcedureTxContextBase, TResult> body) =>
txState.WithTx(body);
public TxOutcome<TResult> TryWithTx<TResult, TError>(
Func<ProcedureTxContextBase, Result<TResult, TError>> body
)
where TError : Exception
{
var outcome = txState.TryWithTx(body);
return outcome.IsSuccess
? TxOutcome<TResult>.Success(outcome.Value!)
: TxOutcome<TResult>.Failure(
outcome.Error
?? new InvalidOperationException("Transaction failed without an error object.")
);
}
}
public abstract class ProcedureTxContextBase(Internal.TxContext inner) : IRefreshableTxContext
{
internal Internal.TxContext Inner { get; private set; } = inner;
internal void Refresh(Internal.TxContext inner) => Inner = inner;
void IRefreshableTxContext.Refresh(Internal.TxContext inner) => Refresh(inner);
public LocalBase Db => (LocalBase)Inner.Db;
public Identity Sender => Inner.Sender;
public ConnectionId? ConnectionId => Inner.ConnectionId;
public Timestamp Timestamp => Inner.Timestamp;
public AuthCtx SenderAuth => Inner.SenderAuth;
public Random Rng => Inner.Rng;
}
public abstract class LocalBase : Internal.Local { }
internal sealed partial class RuntimeProcedureContext(
Identity sender,
ConnectionId? connectionId,
Random random,
Timestamp timestamp
) : ProcedureContextBase(sender, connectionId, random, timestamp)
{
private readonly RuntimeLocal _db = new();
protected internal override LocalBase CreateLocal() => _db;
protected override ProcedureTxContextBase CreateTxContext(Internal.TxContext inner) =>
_cached ??= new RuntimeProcedureTxContext(inner);
private RuntimeProcedureTxContext? _cached;
}
internal sealed class RuntimeProcedureTxContext : ProcedureTxContextBase
{
internal RuntimeProcedureTxContext(Internal.TxContext inner)
: base(inner) { }
public new RuntimeLocal Db => (RuntimeLocal)base.Db;
}
internal sealed class RuntimeLocal : LocalBase { }
#pragma warning restore STDB_UNSTABLE