@cratis/pi 7.101.0 → 7.103.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (168) hide show
  1. package/package/corpus/harnesses/copilot/instructions/github-actions.instructions.md +17 -0
  2. package/package/corpus/harnesses/copilot/instructions/profiles.instructions.md +10 -1
  3. package/package/corpus/harnesses/copilot/instructions/pull-requests.instructions.md +2 -0
  4. package/package/corpus/harnesses/copilot/instructions/secrets.instructions.md +4 -0
  5. package/package/corpus/hooks/README.md +11 -1
  6. package/package/corpus/hooks/scripts/cratis-check-pr.mjs +12 -1
  7. package/package/corpus/hooks/scripts/cratis-guard-secrets.sh +79 -23
  8. package/package/corpus/hooks/scripts/cratis-guard-writes.sh +145 -40
  9. package/package/corpus/rules/github-actions.md +17 -0
  10. package/package/corpus/rules/profiles.md +10 -1
  11. package/package/corpus/rules/pull-requests.md +2 -0
  12. package/package/corpus/rules/secrets.md +4 -0
  13. package/package/corpus/skills/analyzing-dotnet-performance/LICENSE +23 -0
  14. package/package/corpus/skills/analyzing-dotnet-performance/NOTICE +36 -0
  15. package/package/corpus/skills/analyzing-dotnet-performance/SKILL.md +214 -0
  16. package/package/corpus/skills/analyzing-dotnet-performance/references/async-patterns.md +122 -0
  17. package/package/corpus/skills/analyzing-dotnet-performance/references/collections-and-linq.md +220 -0
  18. package/package/corpus/skills/analyzing-dotnet-performance/references/critical-patterns.md +291 -0
  19. package/package/corpus/skills/analyzing-dotnet-performance/references/io-and-serialization.md +135 -0
  20. package/package/corpus/skills/analyzing-dotnet-performance/references/memory-and-strings.md +219 -0
  21. package/package/corpus/skills/analyzing-dotnet-performance/references/regex-patterns.md +95 -0
  22. package/package/corpus/skills/analyzing-dotnet-performance/references/structural-patterns.md +38 -0
  23. package/package/corpus/skills/binlog-generation/LICENSE +23 -0
  24. package/package/corpus/skills/binlog-generation/NOTICE +22 -0
  25. package/package/corpus/skills/binlog-generation/SKILL.md +129 -0
  26. package/package/corpus/skills/check-bin-obj-clash/LICENSE +23 -0
  27. package/package/corpus/skills/check-bin-obj-clash/NOTICE +27 -0
  28. package/package/corpus/skills/check-bin-obj-clash/SKILL.md +127 -0
  29. package/package/corpus/skills/check-bin-obj-clash/references/common-causes-and-fixes.md +206 -0
  30. package/package/corpus/skills/coverage-analysis/LICENSE +23 -0
  31. package/package/corpus/skills/coverage-analysis/NOTICE +32 -0
  32. package/package/corpus/skills/coverage-analysis/SKILL.md +198 -0
  33. package/package/corpus/skills/coverage-analysis/references/guidelines.md +68 -0
  34. package/package/corpus/skills/coverage-analysis/references/output-format.md +91 -0
  35. package/package/corpus/skills/coverage-analysis/references/report-generation.md +58 -0
  36. package/package/corpus/skills/coverage-analysis/references/setup-discovery.md +307 -0
  37. package/package/corpus/skills/coverage-analysis/scripts/Compute-CrapScores.ps1 +80 -0
  38. package/package/corpus/skills/coverage-analysis/scripts/CoverageData.ps1 +272 -0
  39. package/package/corpus/skills/coverage-analysis/scripts/Extract-MethodCoverage.ps1 +65 -0
  40. package/package/corpus/skills/cratis-ci-pipeline-performance/LICENSE +2 -0
  41. package/package/corpus/skills/cratis-ci-pipeline-performance/SKILL.md +201 -0
  42. package/package/corpus/skills/cratis-ci-pipeline-performance/references/build-once-and-shard.md +158 -0
  43. package/package/corpus/skills/cratis-ci-pipeline-performance/references/caching.md +86 -0
  44. package/package/corpus/skills/cratis-ci-pipeline-performance/references/flaky-and-hung-jobs.md +70 -0
  45. package/package/corpus/skills/cratis-ci-pipeline-performance/references/publish-pipelines.md +67 -0
  46. package/package/corpus/skills/cratis-ci-pipeline-performance/references/required-checks-and-path-filters.md +117 -0
  47. package/package/corpus/skills/cratis-ci-pipeline-performance/references/runner-sizing-and-images.md +65 -0
  48. package/package/corpus/skills/cratis-ci-pipeline-performance/references/security-and-pinning.md +73 -0
  49. package/package/corpus/skills/cratis-ci-pipeline-performance/references/sources.md +33 -0
  50. package/package/corpus/skills/cratis-ci-pipeline-performance/references/test-setup-cost.md +82 -0
  51. package/package/corpus/skills/cratis-ci-pipeline-performance/scripts/ci-timings.sh +356 -0
  52. package/package/corpus/skills/cratis-ci-pipeline-performance/scripts/fixtures/jobs/1003.json +79 -0
  53. package/package/corpus/skills/cratis-ci-pipeline-performance/scripts/fixtures/jobs/1007.json +79 -0
  54. package/package/corpus/skills/cratis-ci-pipeline-performance/scripts/fixtures/jobs/1010.json +79 -0
  55. package/package/corpus/skills/cratis-ci-pipeline-performance/scripts/fixtures/runs.json +102 -0
  56. package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/LICENSE +2 -0
  57. package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/SKILL.md +244 -0
  58. package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/references/docker-images.md +86 -0
  59. package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/references/dotnet-nuget.md +87 -0
  60. package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/references/go-jvm-python-elixir.md +83 -0
  61. package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/references/npm-yarn.md +78 -0
  62. package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/references/other-environments.md +60 -0
  63. package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/references/worked-examples.md +97 -0
  64. package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/scripts/cratis-local-packages.sh +778 -0
  65. package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/scripts/npm-restore-check.cjs +109 -0
  66. package/package/corpus/skills/csharp-refactoring/LICENSE +23 -0
  67. package/package/corpus/skills/csharp-refactoring/NOTICE +22 -0
  68. package/package/corpus/skills/csharp-refactoring/SKILL.md +175 -0
  69. package/package/corpus/skills/csharp-refactoring/references/operation-catalog.md +37 -0
  70. package/package/corpus/skills/directory-build-organization/LICENSE +23 -0
  71. package/package/corpus/skills/directory-build-organization/NOTICE +21 -0
  72. package/package/corpus/skills/directory-build-organization/SKILL.md +205 -0
  73. package/package/corpus/skills/directory-build-organization/references/common-patterns.md +56 -0
  74. package/package/corpus/skills/directory-build-organization/references/multi-level-examples.md +167 -0
  75. package/package/corpus/skills/directory-build-organization/references/targetframework-props-pitfall.md +54 -0
  76. package/package/corpus/skills/dotnet-aot-compat/LICENSE +23 -0
  77. package/package/corpus/skills/dotnet-aot-compat/NOTICE +18 -0
  78. package/package/corpus/skills/dotnet-aot-compat/SKILL.md +265 -0
  79. package/package/corpus/skills/dotnet-aot-compat/references/polyfills.md +43 -0
  80. package/package/corpus/skills/dotnet-trace-collect/LICENSE +23 -0
  81. package/package/corpus/skills/dotnet-trace-collect/NOTICE +65 -0
  82. package/package/corpus/skills/dotnet-trace-collect/SKILL.md +116 -0
  83. package/package/corpus/skills/dotnet-trace-collect/references/dotnet-monitor.md +97 -0
  84. package/package/corpus/skills/dotnet-trace-collect/references/dotnet-trace-collect-linux.md +84 -0
  85. package/package/corpus/skills/dotnet-trace-collect/references/dotnet-trace-collect.md +91 -0
  86. package/package/corpus/skills/dotnet-trace-collect/references/perfcollect.md +200 -0
  87. package/package/corpus/skills/dotnet-trace-collect/references/perfview.md +141 -0
  88. package/package/corpus/skills/dotnet-trace-collect/references/tool-selection.md +160 -0
  89. package/package/corpus/skills/dump-collect/LICENSE +23 -0
  90. package/package/corpus/skills/dump-collect/NOTICE +40 -0
  91. package/package/corpus/skills/dump-collect/SKILL.md +97 -0
  92. package/package/corpus/skills/dump-collect/references/container-dumps.md +412 -0
  93. package/package/corpus/skills/dump-collect/references/coreclr-dumps.md +108 -0
  94. package/package/corpus/skills/dump-collect/references/nativeaot-dumps.md +280 -0
  95. package/package/corpus/skills/eliminate-redundant-restores/LICENSE +23 -0
  96. package/package/corpus/skills/eliminate-redundant-restores/NOTICE +19 -0
  97. package/package/corpus/skills/eliminate-redundant-restores/SKILL.md +112 -0
  98. package/package/corpus/skills/extension-points/LICENSE +23 -0
  99. package/package/corpus/skills/extension-points/NOTICE +18 -0
  100. package/package/corpus/skills/extension-points/SKILL.md +248 -0
  101. package/package/corpus/skills/filter-syntax/LICENSE +23 -0
  102. package/package/corpus/skills/filter-syntax/NOTICE +17 -0
  103. package/package/corpus/skills/filter-syntax/SKILL.md +179 -0
  104. package/package/corpus/skills/including-generated-files/LICENSE +23 -0
  105. package/package/corpus/skills/including-generated-files/NOTICE +17 -0
  106. package/package/corpus/skills/including-generated-files/SKILL.md +139 -0
  107. package/package/corpus/skills/microbenchmarking/LICENSE +23 -0
  108. package/package/corpus/skills/microbenchmarking/NOTICE +36 -0
  109. package/package/corpus/skills/microbenchmarking/SKILL.md +131 -0
  110. package/package/corpus/skills/microbenchmarking/references/bdn-internals-and-tuning.md +143 -0
  111. package/package/corpus/skills/microbenchmarking/references/comparison-strategies.md +200 -0
  112. package/package/corpus/skills/microbenchmarking/references/diagnosers-and-exporters.md +150 -0
  113. package/package/corpus/skills/microbenchmarking/references/project-setup-and-running.md +189 -0
  114. package/package/corpus/skills/microbenchmarking/references/writing-benchmarks.md +310 -0
  115. package/package/corpus/skills/msbuild/LICENSE +23 -0
  116. package/package/corpus/skills/msbuild/NOTICE +55 -0
  117. package/package/corpus/skills/msbuild/SKILL.md +117 -0
  118. package/package/corpus/skills/msbuild/references/antipatterns/additional-antipatterns.md +291 -0
  119. package/package/corpus/skills/msbuild/references/antipatterns/incremental-build-inputs-outputs.md +57 -0
  120. package/package/corpus/skills/msbuild/references/antipatterns/private-assets.md +29 -0
  121. package/package/corpus/skills/msbuild/references/binlog-failure-analysis.md +85 -0
  122. package/package/corpus/skills/msbuild/references/binlog-generation.md +69 -0
  123. package/package/corpus/skills/msbuild/references/build-perf-baseline.md +493 -0
  124. package/package/corpus/skills/msbuild/references/build-perf-diagnostics.md +268 -0
  125. package/package/corpus/skills/msbuild/references/copy-to-output-directory.md +183 -0
  126. package/package/corpus/skills/msbuild/references/eval-performance.md +190 -0
  127. package/package/corpus/skills/msbuild/references/extension-points.md +281 -0
  128. package/package/corpus/skills/msbuild/references/incremental-build.md +365 -0
  129. package/package/corpus/skills/msbuild/references/msbuild-antipatterns.md +401 -0
  130. package/package/corpus/skills/orleans/LICENSE +21 -0
  131. package/package/corpus/skills/orleans/NOTICE +64 -0
  132. package/package/corpus/skills/orleans/SKILL.md +140 -0
  133. package/package/corpus/skills/orleans/references/anti-patterns.md +660 -0
  134. package/package/corpus/skills/orleans/references/configuration-api.md +933 -0
  135. package/package/corpus/skills/orleans/references/examples.md +66 -0
  136. package/package/corpus/skills/orleans/references/grain-api.md +784 -0
  137. package/package/corpus/skills/orleans/references/grains.md +75 -0
  138. package/package/corpus/skills/orleans/references/hosting.md +71 -0
  139. package/package/corpus/skills/orleans/references/implementation.md +591 -0
  140. package/package/corpus/skills/orleans/references/official-docs-index.md +250 -0
  141. package/package/corpus/skills/orleans/references/patterns.md +811 -0
  142. package/package/corpus/skills/orleans/references/persistence-api.md +495 -0
  143. package/package/corpus/skills/orleans/references/primitive-selection.md +35 -0
  144. package/package/corpus/skills/orleans/references/scheduling-and-services.md +295 -0
  145. package/package/corpus/skills/orleans/references/serialization-api.md +299 -0
  146. package/package/corpus/skills/orleans/references/streaming-api.md +396 -0
  147. package/package/corpus/skills/orleans/references/testing-patterns.md +210 -0
  148. package/package/corpus/skills/platform-detection/LICENSE +23 -0
  149. package/package/corpus/skills/platform-detection/NOTICE +17 -0
  150. package/package/corpus/skills/platform-detection/SKILL.md +230 -0
  151. package/package/corpus/skills/platform-detection/references/command-mode.md +27 -0
  152. package/package/corpus/skills/profiling/LICENSE +21 -0
  153. package/package/corpus/skills/profiling/NOTICE +50 -0
  154. package/package/corpus/skills/profiling/SKILL.md +144 -0
  155. package/package/corpus/skills/profiling/references/commands.md +439 -0
  156. package/package/corpus/skills/profiling/references/patterns.md +586 -0
  157. package/package/corpus/skills/profiling/references/profiling.md +190 -0
  158. package/package/corpus/skills/run-tests/LICENSE +23 -0
  159. package/package/corpus/skills/run-tests/NOTICE +19 -0
  160. package/package/corpus/skills/run-tests/SKILL.md +305 -0
  161. package/package/corpus/skills/target-authoring/LICENSE +23 -0
  162. package/package/corpus/skills/target-authoring/NOTICE +17 -0
  163. package/package/corpus/skills/target-authoring/SKILL.md +165 -0
  164. package/package/corpus/skills/vectorization/LICENSE +23 -0
  165. package/package/corpus/skills/vectorization/NOTICE +17 -0
  166. package/package/corpus/skills/vectorization/SKILL.md +189 -0
  167. package/package/profile-catalog.json +87 -1
  168. package/package.json +3 -2
@@ -0,0 +1,495 @@
1
+ # Persistence, Databases, Journaling, Event Sourcing, and Transactions API
2
+
3
+ ## Contents
4
+
5
+ - State Ownership and Database Boundary
6
+ - IPersistentState API
7
+ - Storage Provider Configuration
8
+ - Experimental Orleans.Journaling
9
+ - Event Sourcing with JournaledGrain
10
+ - ACID Transactions
11
+
12
+ Detailed state management API patterns from official Orleans documentation.
13
+
14
+ ## State Ownership and Database Boundary
15
+
16
+ Use Orleans persistence to preserve state owned by a logical grain identity. The normal model is:
17
+
18
+ 1. Orleans activates the grain and loads its named state before application calls are processed.
19
+ 2. Grain code reads and mutates the in-memory state during serialized turns.
20
+ 3. Grain code explicitly calls `WriteStateAsync` when the new state must become durable.
21
+ 4. The storage provider persists an opaque state record keyed by grain identity and state name.
22
+
23
+ This is an entity-state API, not a general database abstraction. Select it for bounded current state addressed by grain identity. Select an application database/repository or read model for:
24
+
25
+ - joins, search, reports, arbitrary filters, or scans across many identities;
26
+ - large/unbounded collections, time-series, files, and append-only history;
27
+ - set-based updates and database-native constraints;
28
+ - data shared with non-Orleans applications;
29
+ - an existing external system of record.
30
+
31
+ It is common to use both. Let the grain own command serialization, invariants, workflow position, idempotency keys, and a small current snapshot. Let a database own query projections, history, reporting rows, or large payloads. Define which side is authoritative for each field and how projections recover after failure.
32
+
33
+ Do not:
34
+
35
+ - query another grain's storage row instead of calling its contract;
36
+ - mutate provider tables/containers behind an active grain;
37
+ - assume that using an ADO.NET or Cosmos provider turns grain state into a supported application query model;
38
+ - store an unbounded child collection in one grain because the provider can serialize it;
39
+ - keep two writable copies without conflict/reconciliation rules.
40
+
41
+ When external data is authoritative, keep only a cache plus its version/staleness metadata in the grain, or re-read the external repository at the consistency points required by the domain.
42
+
43
+ ## IPersistentState API
44
+
45
+ ```csharp
46
+ public class PlayerGrain : Grain, IPlayerGrain
47
+ {
48
+ private readonly IPersistentState<PlayerProfile> _profile;
49
+ private readonly IPersistentState<PlayerInventory> _inventory;
50
+
51
+ public PlayerGrain(
52
+ [PersistentState("profile", "profileStore")]
53
+ IPersistentState<PlayerProfile> profile,
54
+ [PersistentState("inventory", "itemStore")]
55
+ IPersistentState<PlayerInventory> inventory)
56
+ {
57
+ _profile = profile;
58
+ _inventory = inventory;
59
+ }
60
+ }
61
+ ```
62
+
63
+ ### Operations
64
+
65
+ | Operation | Behavior |
66
+ |---|---|
67
+ | `state.State` | Access/modify in-memory state |
68
+ | `state.ReadStateAsync()` | Reload from storage (automatic on activation) |
69
+ | `state.WriteStateAsync()` | Persist current state (must call explicitly) |
70
+ | `state.ClearStateAsync()` | Clear/delete from storage |
71
+ | `state.RecordExists` | Whether state exists in storage |
72
+ | `state.Etag` | Provider-specific optimistic concurrency token |
73
+
74
+ ### State Type Requirements
75
+
76
+ ```csharp
77
+ [GenerateSerializer]
78
+ public class PlayerProfile
79
+ {
80
+ [Id(0)] public string Name { get; set; } = "";
81
+ [Id(1)] public int Level { get; set; }
82
+ [Id(2)] public DateTime LastLogin { get; set; }
83
+ }
84
+ ```
85
+
86
+ - Use `[GenerateSerializer]` and `[Id(N)]` on all state types
87
+ - Use JSON or version-tolerant format for stored state
88
+ - Adding new `[Id]` members is safe; removing is safe if ID not reused; changing types is breaking
89
+
90
+ ## Storage Provider Configuration
91
+
92
+ ### Redis
93
+
94
+ ```csharp
95
+ siloBuilder.AddRedisGrainStorage("redis", options =>
96
+ {
97
+ options.ConfigurationOptions = new ConfigurationOptions
98
+ {
99
+ EndPoints = { "localhost:6379" }
100
+ };
101
+ options.DeleteStateOnClear = true;
102
+ options.EntryExpiry = TimeSpan.FromDays(30);
103
+ });
104
+ ```
105
+
106
+ ### Azure Cosmos DB
107
+
108
+ ```csharp
109
+ siloBuilder.AddCosmosGrainStorage("cosmos", options =>
110
+ {
111
+ options.DatabaseName = "OrleansState";
112
+ options.ContainerName = "GrainState";
113
+ options.IsResourceCreationEnabled = true;
114
+ options.DeleteStateOnClear = true;
115
+ options.StateFieldsToIndex = new[] { "/State/Type" };
116
+ });
117
+ ```
118
+
119
+ ### Azure Table/Blob Storage
120
+
121
+ ```csharp
122
+ siloBuilder.AddAzureTableGrainStorage("table", options =>
123
+ options.ConfigureTableServiceClient(connectionString));
124
+
125
+ siloBuilder.AddAzureBlobGrainStorage("blob", options =>
126
+ options.ConfigureBlobServiceClient(connectionString));
127
+ ```
128
+
129
+ ### ADO.NET
130
+
131
+ ```csharp
132
+ siloBuilder.AddAdoNetGrainStorage("sql", options =>
133
+ {
134
+ options.Invariant = "Microsoft.Data.SqlClient";
135
+ options.ConnectionString = connectionString;
136
+ });
137
+ ```
138
+
139
+ ### Amazon DynamoDB
140
+
141
+ ```csharp
142
+ siloBuilder.AddDynamoDBGrainStorage("dynamo", options =>
143
+ {
144
+ options.Service = "us-east-1";
145
+ options.AccessKey = accessKey;
146
+ options.SecretKey = secretKey;
147
+ options.TableName = "OrleansGrainState";
148
+ options.CreateIfNotExists = true;
149
+ options.DeleteStateOnClear = true;
150
+ });
151
+ ```
152
+
153
+ ### Storage Provider Summary
154
+
155
+ | Provider | Package | Config Method |
156
+ |---|---|---|
157
+ | Redis | `Microsoft.Orleans.Persistence.Redis` | `AddRedisGrainStorage` |
158
+ | Azure Table | `Microsoft.Orleans.Persistence.AzureStorage` | `AddAzureTableGrainStorage` |
159
+ | Azure Blob | `Microsoft.Orleans.Persistence.AzureStorage` | `AddAzureBlobGrainStorage` |
160
+ | Cosmos DB | `Microsoft.Orleans.Persistence.Cosmos` | `AddCosmosGrainStorage` |
161
+ | ADO.NET | `Microsoft.Orleans.Persistence.AdoNet` | `AddAdoNetGrainStorage` |
162
+ | DynamoDB | `Microsoft.Orleans.Persistence.DynamoDB` | `AddDynamoDBGrainStorage` |
163
+ | Memory (dev) | built-in | `AddMemoryGrainStorage` |
164
+
165
+ ### Custom Storage Provider
166
+
167
+ ```csharp
168
+ public class MyStorage : IGrainStorage
169
+ {
170
+ public Task ReadStateAsync<T>(string stateName, GrainId grainId, IGrainState<T> grainState) { }
171
+ public Task WriteStateAsync<T>(string stateName, GrainId grainId, IGrainState<T> grainState) { }
172
+ public Task ClearStateAsync<T>(string stateName, GrainId grainId, IGrainState<T> grainState) { }
173
+ }
174
+ ```
175
+
176
+ ## Experimental Orleans.Journaling
177
+
178
+ `Microsoft.Orleans.Journaling` is a separate experimental persistence surface in Orleans 10.3. It records durable state operations in an ordered journal and replays them to recover durable collections and values. The `10.3.1` package is published as `10.3.1-alpha.1`, so require an explicit adoption decision and version pin.
179
+
180
+ Use it when operation-journaled durable collections or completion state solve a concrete need. Available durable shapes include dictionary, list, queue, set, value, persistent state, and durable task completion source. Do not confuse it with `JournaledGrain<TState,TEvent>`:
181
+
182
+ | Surface | Purpose | Entry model |
183
+ |---|---|---|
184
+ | `IPersistentState<T>` | Persist the current entity snapshot | Application writes the current state |
185
+ | `Orleans.Journaling` | Persist/replay durable collection/value operations | Framework durable-operation codecs |
186
+ | `JournaledGrain<TState,TEvent>` | Event-source domain state | Application-defined business events |
187
+
188
+ Install and configure a journaling provider on the same Orleans version:
189
+
190
+ ```bash
191
+ dotnet add package Microsoft.Orleans.Journaling --version 10.3.1-alpha.1
192
+ dotnet add package Microsoft.Orleans.Journaling.AzureStorage --version 10.3.1-alpha.1
193
+ ```
194
+
195
+ ```csharp
196
+ [JsonSerializable(typeof(string))]
197
+ [JsonSerializable(typeof(int))]
198
+ internal partial class JournalJsonContext : JsonSerializerContext;
199
+
200
+ builder.UseOrleans(siloBuilder =>
201
+ siloBuilder
202
+ .AddAzureBlobJournalStorage()
203
+ .UseJsonJournalFormat(JournalJsonContext.Default));
204
+ ```
205
+
206
+ Use durable keyed state from a `DurableGrain` and commit through `WriteStateAsync`:
207
+
208
+ ```csharp
209
+ public sealed class CartGrain(
210
+ [FromKeyedServices("cart")] IDurableDictionary<string, int> items)
211
+ : DurableGrain, ICartGrain
212
+ {
213
+ public async ValueTask SetQuantityAsync(string itemId, int quantity)
214
+ {
215
+ items[itemId] = quantity;
216
+ await WriteStateAsync();
217
+ }
218
+ }
219
+ ```
220
+
221
+ Orleans 10.2 uses JSON Lines for new journal writes by default. Existing records with format metadata remain readable; records without metadata are treated as legacy Orleans binary. Pin `JournaledStateManagerOptions.JournalFormatKey = "orleans-binary"` only when a staged compatibility plan requires continuing old-format writes.
222
+
223
+ For trimming or Native AOT, register source-generated `System.Text.Json` metadata for every journaled key, value, and state type. Test replay, compaction/snapshot migration, provider failure, and version upgrade with production-like storage.
224
+
225
+ Official package sources:
226
+
227
+ - [Orleans.Journaling README for 10.3.1](https://github.com/dotnet/orleans/blob/v10.3.1/src/Orleans.Journaling/README.md)
228
+ - [Azure Storage Journaling README for 10.3.1](https://github.com/dotnet/orleans/blob/v10.3.1/src/Azure/Orleans.Journaling.AzureStorage/README.md)
229
+ - [Orleans 10.3.0 release notes](https://github.com/dotnet/orleans/releases/tag/v10.3.0)
230
+
231
+ ## Event Sourcing with JournaledGrain
232
+
233
+ ```csharp
234
+ // State class
235
+ [GenerateSerializer]
236
+ public class BankAccountState
237
+ {
238
+ [Id(0)] public decimal Balance { get; set; }
239
+
240
+ // Apply methods — called automatically by the runtime
241
+ public void Apply(DepositEvent e) => Balance += e.Amount;
242
+ public void Apply(WithdrawEvent e) => Balance -= e.Amount;
243
+ }
244
+
245
+ // Event base
246
+ [GenerateSerializer]
247
+ public abstract record AccountEvent;
248
+
249
+ [GenerateSerializer]
250
+ public record DepositEvent([property: Id(0)] decimal Amount) : AccountEvent;
251
+
252
+ [GenerateSerializer]
253
+ public record WithdrawEvent([property: Id(0)] decimal Amount) : AccountEvent;
254
+
255
+ // Grain
256
+ public class BankAccountGrain : JournaledGrain<BankAccountState, AccountEvent>, IBankAccountGrain
257
+ {
258
+ public Task Deposit(decimal amount)
259
+ {
260
+ RaiseEvent(new DepositEvent(amount));
261
+ return ConfirmEvents(); // immediate confirmation
262
+ }
263
+
264
+ public Task<decimal> GetBalance() => Task.FromResult(State.Balance);
265
+ }
266
+ ```
267
+
268
+ ### Confirmation Modes
269
+
270
+ | Mode | Method | Tradeoff |
271
+ |---|---|---|
272
+ | Immediate | `ConfirmEvents()` after `RaiseEvent` | Consistent but slower |
273
+ | Delayed | Just `RaiseEvent`, no confirm | Higher throughput, eventual consistency |
274
+ | Conditional | Confirm at checkpoints | Balance of both |
275
+
276
+ ### Log Consistency Providers
277
+
278
+ Three built-in providers. Configure via silo builder:
279
+
280
+ ```csharp
281
+ siloBuilder.AddLogStorageBasedLogConsistencyProvider("LogStorage");
282
+ siloBuilder.AddStateStorageBasedLogConsistencyProvider("StateStorage");
283
+ siloBuilder.AddCustomStorageBasedLogConsistencyProvider("CustomStorage");
284
+ ```
285
+
286
+ ### Raising Multiple Events
287
+
288
+ ```csharp
289
+ // Individual raises — two storage accesses, risk of partial write
290
+ RaiseEvent(e1);
291
+ RaiseEvent(e2);
292
+ await ConfirmEvents();
293
+
294
+ // Atomic batch — written atomically
295
+ RaiseEvents(new[] { e1, e2 });
296
+ await ConfirmEvents();
297
+ ```
298
+
299
+ ### Retrieving Events
300
+
301
+ ```csharp
302
+ // Not all providers support this (throws NotSupportedException if unavailable)
303
+ IReadOnlyList<AccountEvent> events = await RetrieveConfirmedEvents(0, Version);
304
+ ```
305
+
306
+ ### Immediate vs Delayed Confirmation
307
+
308
+ **Immediate**: call `ConfirmEvents()` after every `RaiseEvent`. Grain unavailable during storage issues. Safe, consistent.
309
+
310
+ **Delayed**: raise events without confirming. Higher throughput but eventual consistency.
311
+
312
+ ```csharp
313
+ // Delayed — access tentative state
314
+ IEnumerable<AccountEvent> UnconfirmedEvents { get; }
315
+ AccountState TentativeState { get; } // State + all unconfirmed events applied
316
+ ```
317
+
318
+ `TentativeState` is a best guess — events may be canceled or reordered in multi-instance scenarios.
319
+
320
+ ### Conditional Events (Conflict Resolution)
321
+
322
+ ```csharp
323
+ // Returns false if race lost (version mismatch)
324
+ bool success = await RaiseConditionalEvent(new WithdrawEvent { Amount = 100 });
325
+ // Awaiting the task is sufficient confirmation — no need for ConfirmEvents
326
+ ```
327
+
328
+ Analogous to e-tag conditional updates. Use for operations that must not silently conflict (withdrawals). Unconditional events (deposits) always append.
329
+
330
+ ### Notifications
331
+
332
+ ```csharp
333
+ // Called when confirmed state changes (load, write success, remote notification)
334
+ protected override void OnStateChanged() { }
335
+
336
+ // Called when tentative state changes (including during RaiseEvent)
337
+ protected override void OnTentativeStateChanged() { }
338
+ ```
339
+
340
+ ### Explicit Synchronization
341
+
342
+ ```csharp
343
+ await RefreshNow(); // confirms all unconfirmed events + loads latest from storage
344
+ ```
345
+
346
+ ### Diagnostics
347
+
348
+ ```csharp
349
+ EnableStatsCollection();
350
+ LogConsistencyStatistics stats = GetStats();
351
+ DisableStatsCollection();
352
+
353
+ // Connection error monitoring
354
+ protected override void OnConnectionIssue(ConnectionIssue issue) { }
355
+ protected override void OnConnectionIssueResolved(ConnectionIssue issue) { }
356
+ ```
357
+
358
+ ### Log Consistency Providers
359
+
360
+ | Provider | Storage | RetrieveConfirmedEvents | Use Case |
361
+ |---|---|---|---|
362
+ | `StateStorage` | State snapshot via standard storage provider | No (events not persisted) | Production — snapshot-based |
363
+ | `LogStorage` | Complete event list as single object | Yes (all events in memory) | Testing only — full read/write each time |
364
+ | `CustomStorage` | Grain implements `ICustomStorageInterface<S,E>` | No (you control storage) | Production — custom backends |
365
+
366
+ ```csharp
367
+ // Register providers
368
+ siloBuilder.AddStateStorageBasedLogConsistencyProvider("StateStorage");
369
+ siloBuilder.AddLogStorageBasedLogConsistencyProvider("LogStorage");
370
+ siloBuilder.AddCustomStorageBasedLogConsistencyProvider("CustomStorage");
371
+
372
+ // Attribute on grain
373
+ [StorageProvider(ProviderName = "OrleansStorage")]
374
+ [LogConsistencyProvider(ProviderName = "StateStorage")]
375
+ public class MyGrain : JournaledGrain<MyState, MyEvent> { }
376
+ ```
377
+
378
+ ### CustomStorage Interface
379
+
380
+ ```csharp
381
+ public interface ICustomStorageInterface<StateType, EventType>
382
+ {
383
+ Task<KeyValuePair<int, StateType>> ReadStateFromStorage();
384
+ // Return false on version mismatch (e-tag semantics)
385
+ Task<bool> ApplyUpdatesToStorage(IReadOnlyList<EventType> updates, int expectedVersion);
386
+ }
387
+ ```
388
+
389
+ ### Replicated Instances
390
+
391
+ Multi-cluster event sourcing guarantees:
392
+ - Same version number = same state across instances
393
+ - Racing events resolved into one agreed sequence
394
+ - Notifications propagated after events raised
395
+
396
+ ## ACID Transactions
397
+
398
+ ```mermaid
399
+ flowchart LR
400
+ C["Client / Grain"] -->|"RunTransaction"| T["TransactionClient"]
401
+ T -->|"Create tx context"| G1["Grain A<br/>[Reentrant]"]
402
+ T -->|"Join tx context"| G2["Grain B<br/>[Reentrant]"]
403
+ G1 -->|"PerformUpdate"| S1["ITransactionalState&lt;T&gt;"]
404
+ G2 -->|"PerformUpdate"| S2["ITransactionalState&lt;T&gt;"]
405
+ S1 & S2 -->|"Commit / Abort"| T
406
+ ```
407
+
408
+ ### Setup
409
+
410
+ ```csharp
411
+ // Silo
412
+ siloBuilder.UseTransactions();
413
+
414
+ // Client
415
+ clientBuilder.UseTransactions();
416
+
417
+ // Storage
418
+ siloBuilder.AddAzureTableTransactionalStateStorage("TransactionStore",
419
+ options => options.ConfigureTableServiceClient(connectionString));
420
+ ```
421
+
422
+ ### Transaction Attributes
423
+
424
+ | Attribute | Meaning |
425
+ |---|---|
426
+ | `[Transaction(TransactionOption.Create)]` | Always starts new transaction |
427
+ | `[Transaction(TransactionOption.Join)]` | Must be within existing transaction |
428
+ | `[Transaction(TransactionOption.CreateOrJoin)]` | Joins existing or creates new |
429
+ | `[Transaction(TransactionOption.Suppress)]` | Not transactional |
430
+ | `[Transaction(TransactionOption.Supported)]` | Not transactional but passes context |
431
+ | `[Transaction(TransactionOption.NotAllowed)]` | Cannot be called in transaction |
432
+
433
+ ### Grain Implementation
434
+
435
+ ```csharp
436
+ // Interface
437
+ public interface IAccountGrain : IGrainWithStringKey
438
+ {
439
+ [Transaction(TransactionOption.Join)]
440
+ Task Withdraw(decimal amount);
441
+
442
+ [Transaction(TransactionOption.Join)]
443
+ Task Deposit(decimal amount);
444
+
445
+ [Transaction(TransactionOption.CreateOrJoin)]
446
+ Task<decimal> GetBalance();
447
+ }
448
+
449
+ // Grain — MUST be [Reentrant]
450
+ [Reentrant]
451
+ public class AccountGrain : Grain, IAccountGrain
452
+ {
453
+ private readonly ITransactionalState<AccountBalance> _balance;
454
+
455
+ public AccountGrain(
456
+ [TransactionalState("balance", "TransactionStore")]
457
+ ITransactionalState<AccountBalance> balance)
458
+ {
459
+ _balance = balance;
460
+ }
461
+
462
+ public Task Withdraw(decimal amount) =>
463
+ _balance.PerformUpdate(state =>
464
+ {
465
+ if (state.Value < amount)
466
+ throw new InsufficientFundsException();
467
+ state.Value -= amount;
468
+ });
469
+
470
+ public Task Deposit(decimal amount) =>
471
+ _balance.PerformUpdate(state => state.Value += amount);
472
+
473
+ public Task<decimal> GetBalance() =>
474
+ _balance.PerformRead(state => state.Value);
475
+ }
476
+ ```
477
+
478
+ ### Client-Side Transactions
479
+
480
+ ```csharp
481
+ var transactionClient = serviceProvider.GetRequiredService<ITransactionClient>();
482
+
483
+ await transactionClient.RunTransaction(TransactionOption.Create, async () =>
484
+ {
485
+ await fromAccount.Withdraw(100m);
486
+ await toAccount.Deposit(100m);
487
+ });
488
+ ```
489
+
490
+ ### Error Handling
491
+
492
+ - `OrleansTransactionException` wraps app exceptions
493
+ - `OrleansTransactionAbortedException` is retryable
494
+ - Unknown-state: wait for `SystemResponseTimeout` before retrying
495
+ - `OrleansTransactionsDisabledException` if `UseTransactions()` not called
@@ -0,0 +1,35 @@
1
+ # Communication, execution, and placement selection
2
+
3
+ ## Choose Communication
4
+
5
+ | Primitive | Purpose | Choose it when | Failure/ownership model |
6
+ |---|---|---|---|
7
+ | Direct grain call | Typed request/response to a known logical owner | The caller needs completion, a result, or an exception | At-most-once by default; a timeout does not prove whether the target committed a side effect |
8
+ | `IAsyncEnumerable<T>` | Stream a response within one caller-initiated request | One caller consumes progressive results with cancellation and backpressure | Call-scoped, not pub/sub, not a durable subscription |
9
+ | Orleans stream | Decouple producers and consumers across dynamic, long-lived event flows | Pub/sub, multiple consumers, provider-backed queues, replay, or durable subscriptions are needed | Delivery, ordering, replay, and backpressure depend on the selected provider |
10
+ | Broadcast channel | Transient, low-overhead fan-out to implicit grain subscribers | All interested grains need the latest signal and occasional loss/history gaps are acceptable | Best-effort, no message storage, no replay; not a persistent stream |
11
+ | Observer | Push callbacks to a connected client or addressable subscriber | A UI/client needs live notifications and can resubscribe after reconnect | Ephemeral and inherently unreliable for clients; expire, unsubscribe, and delete references |
12
+ | One-way request | Remove the response path for a specialized lossy notification | The sender needs no result, completion, or error and measured response overhead matters | No acknowledgement and no guarantee the callee received the request |
13
+ | `RequestContext` | Flow small request-scoped metadata | Trace, correlation, tenant, or request metadata must follow a call chain | Not durable state; metadata does not flow back in responses |
14
+ | Grain call filter | Apply cross-cutting behavior around calls | Authorization, telemetry, argument/result inspection, or exception conversion spans many grains | Keep domain decisions in grains, not global filters |
15
+ | Grain extension | Attach a runtime/protocol capability to an addressable grain | Infrastructure needs an additional interface without changing the domain interface | Advanced runtime mechanism; avoid as ordinary domain composition |
16
+
17
+ Prefer a direct call unless decoupling is a requirement. Do not use a stream merely to avoid calling a known target. Do not use broadcast channels for commands, money movement, audit events, or anything that must be replayed. Do not use observers as a durable event bus.
18
+
19
+ When adding retries, make side effects idempotent. Default Orleans calls are at-most-once only while neither the runtime nor application retries. Retried calls can arrive more than once, and Orleans does not durably deduplicate them for the application.
20
+
21
+
22
+ ## Choose Execution and Scaling
23
+
24
+ | Primitive | Purpose | Decision rule |
25
+ |---|---|---|
26
+ | Standard grain | Serialize behavior for one identity | Default for stateful domain entities and digital twins |
27
+ | `[StatelessWorker]` | Scale fungible work locally and across silos | Use when activations are interchangeable and their local state need not agree |
28
+ | `[ReadOnly]` | Permit compatible reads to interleave | Use only when the method cannot mutate grain state or external invariants |
29
+ | `[Reentrant]` | Allow turns from other calls while the grain awaits | Use for call cycles or measured concurrency needs after auditing every invariant |
30
+ | `[AlwaysInterleave]` / `[MayInterleave]` | Selectively admit interleaving | Prefer narrow scheduling exceptions over making the whole grain reentrant |
31
+ | Placement strategy/filter | Constrain or optimize activation location | Keep the resource-optimized default unless locality, hardware, zone, compliance, or role requirements are proven |
32
+ | Placement hint | Suggest a target silo for activation or migration | Choose an active compatible silo and scope the request-context hint; it does not move an existing activation |
33
+ | Heterogeneous silo/versioning | Run different grain sets or versions during rollout | Use explicit compatibility/version selection for safe rolling deployments |
34
+
35
+ Turn-based execution is single-threaded, not magically race-free. Reentrancy allows another turn to run while the first awaits; any state observed before the `await` can be stale afterward. Avoid blocking calls, `.Result`, `.Wait()`, thread-affine work, and unbounded CPU loops on the grain scheduler.