feat: add file-based grain storage provider - #9537
Conversation
|
@dotnet-policy-service agree |
|
👍🏻 |
|
I need to find the time to get more information about your suggested changes. i am not that deep into the details you mention and i dont want to submit changes i dont understand. |
|
Hey! Interesting. Is this meant to be used in a single silo or by having shared storage like NFS/SMB/EFS? Or do you plan on have some sort of replication/synchronization/distributed locking on the files itself? I'm interested on what would happen when silos are scaled. Can you elaborate more on that on the PR itself and/or the README you added? Thanks! |
|
Hey @galvesribeiro, in the beginning it was meant to be some more single silo or on prem local smaller solution. I got a few great comments for possible improvements, but they maybe just dont match what this storage provider wants to be. Im gonna need to think and have some talks with @ReubenBond and his team on what we really should aim for. |
There was a problem hiding this comment.
Pull request overview
This PR introduces a new file-based grain storage provider (Orleans.Persistence.FileStorage) for Orleans, including the provider implementation, DI/hosting extensions, packaging metadata, and a README with usage examples.
Changes:
- Added a new persistence provider project/package (
Microsoft.Orleans.Persistence.FileStorage) and included it in the solution. - Implemented the provider (read/write/clear + factory/options) and exposed
AddFileGrainStorage(...)builder/service-collection extensions. - Added initial end-user documentation (
README.md) with configuration and usage examples.
Show a summary per file
| File | Description |
|---|---|
| Orleans.sln | Adds the new project to the solution under a new “File” solution folder. |
| src/File/Orleans.Persistence.FileStorage/Orleans.Persistence.FileStorage.csproj | Defines the new package/project metadata and references. |
| src/File/Orleans.Persistence.FileStorage/FileSiloBuilderExtensions.cs | Adds DI/hosting extension methods for configuring the provider. |
| src/File/Orleans.Persistence.FileStorage/FileGrainStorageOptions.cs | Introduces options for root directory + serializer selection. |
| src/File/Orleans.Persistence.FileStorage/FileGrainStorageFactory.cs | Factory to construct provider instances from DI + named options. |
| src/File/Orleans.Persistence.FileStorage/FileGrainStorage.cs | Core file-backed implementation of IGrainStorage + lifecycle init. |
| src/File/Orleans.Persistence.FileStorage/README.md | Documents installation and provides configuration/usage examples. |
Review details
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Suppressed comments (2)
src/File/Orleans.Persistence.FileStorage/FileGrainStorage.cs:68
- ReadStateAsync reads the state file as text (OpenText/ReadToEndAsync) and then constructs BinaryData from a string. Since WriteStateAsync writes raw bytes, this will corrupt non-text payloads and can fail depending on encoding. Also, when the file is missing, ETag/RecordExists should be reset to avoid leaking previous values.
using var stream = fileInfo.OpenText();
var storedData = await stream.ReadToEndAsync();
grainState.State = options.GrainStorageSerializer.Deserialize<T>(new BinaryData(storedData));
grainState.ETag = fileInfo.LastWriteTimeUtc.ToString(CultureInfo.InvariantCulture);
grainState.RecordExists = true;
src/File/Orleans.Persistence.FileStorage/FileGrainStorage.cs:96
- WriteStateAsync updates grainState.ETag but never sets grainState.RecordExists=true after a successful write, and it formats the ETag using DateTime.ToString(InvariantCulture), which can lose precision and cause missed conflict detection.
if (fileInfo.Exists && fileInfo.LastWriteTimeUtc.ToString(CultureInfo.InvariantCulture) != grainState.ETag)
{
throw new InconsistentStateException($"""
Version conflict (WriteState): ServiceId={_clusterOptions.ServiceId}
ProviderName={storageName} GrainType={typeof(T)}
GrainReference={grainId}.
""");
}
await File.WriteAllBytesAsync(path, storedData.ToArray());
fileInfo.Refresh();
grainState.ETag = fileInfo.LastWriteTimeUtc.ToString(CultureInfo.InvariantCulture);
}
- Files reviewed: 7/7 changed files
- Comments generated: 7
- Review effort level: Lite
7126d93 to
068ba21
Compare
d7b554e to
a9f6eb1
Compare
Code coverage51.93% line coverage - 52,484 / 101,072 lines Coverage details
|
This pull request introduces a new file-based grain storage provider for Microsoft Orleans. The changes include adding a new project for the provider, implementing its core functionality, and providing documentation and configuration examples.
New File-Based Grain Storage Provider
Project Setup:
Orleans.Persistence.FileStorageto the solution with the necessary project references and metadata (Orleans.sln,src/File/Orleans.Persistence.FileStorage/Orleans.Persistence.FileStorage.csproj) [1] [2].Core Implementation:
FileGrainStorageclass, which provides methods for reading, writing, and clearing grain state using a file-based approach (src/File/Orleans.Persistence.FileStorage/FileGrainStorage.cs).FileGrainStorageFactoryto create instances ofFileGrainStorage(src/File/Orleans.Persistence.FileStorage/FileGrainStorageFactory.cs).FileGrainStorageOptionsto configure the root directory and serializer for the storage provider (src/File/Orleans.Persistence.FileStorage/FileGrainStorageOptions.cs).FileSiloBuilderExtensionsto simplify the configuration of the file storage provider in Orleans silo builders (src/File/Orleans.Persistence.FileStorage/FileSiloBuilderExtensions.cs).Documentation and Examples:
README.mdfile with an introduction, setup instructions, and examples for configuring and using the file storage provider (src/File/Orleans.Persistence.FileStorage/README.md).Microsoft Reviewers: Open in CodeFlow