Skip to content

fix(adonet): return scalar SQLite persistence versions - #11322

Merged
ReubenBond merged 2 commits into
dotnet:mainfrom
ReubenBond:rb-11303-sqlite-persistence-version-rows
Sep 18, 2026
Merged

ReubenBond merged 2 commits into
dotnet:mainfrom
ReubenBond:rb-11303-sqlite-persistence-version-rows

Conversation

@ReubenBond

@ReubenBond ReubenBond commented Sep 18, 2026 •

Copy link
Copy Markdown
Member

Problem

SQLite persistence reads the successful write/clear version back from every OrleansStorage row with the same full grain identity. Duplicate identities therefore produce multiple result rows, violating AdoNetGrainStorage's single-result contract. With unequal duplicate versions, the result can also include a version from a row which the mutation did not match.

Solution

Compute the successful result from the mutation: 1 for a null-version insert, and the expected version plus one for a version-matched update or clear. Keep the version-qualified mutations, guarded insert, transaction boundaries, and conflict-result branches unchanged.

Add isolated regressions which execute the installed SQLite query batches and consume all result sets, covering equal, unequal, and null duplicate versions, hash collisions, null-version inserts, stale/missing versions, and write-clear-write progression. Document updating both stored OrleansQuery entries and restarting providers for existing databases.

The full-identity uniqueness contract remains the same. Duplicate-data investigation and reconciliation remain a separate maintenance responsibility.

Refs #11303.

Fixes: #11303

Microsoft Reviewers: Open in CodeFlow

Copilot AI lite review requested due to automatic review settings September 18, 2026 09:10

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟢 Approval recommended

The query changes preserve mutation and conflict behavior while ensuring a single scalar result, with comprehensive regression coverage.

Review effort: Lite
Findings: None

What changed in this PR

Fixes SQLite ADO.NET persistence version results by returning scalar mutation-derived versions, preventing duplicate storage rows from producing multiple results.

Changes:

  • Updated SQLite write and clear queries.
  • Added isolated regression tests for versions, duplicates, conflicts, and hash collisions.
  • Documented query updates for existing databases.
File Description
Sqlite-Persistence.sql Computes scalar persistence versions.
SqlitePersistenceQueryTests.cs Adds SQLite query regressions.
relational-storage.md Documents the contract and migration process.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@github-actions

github-actions Bot commented Sep 18, 2026 •

Copy link
Copy Markdown
Contributor

Code coverage

Metric Pull request
Lines 82.23% (112,506 / 136,817)
Branches 71.50% (32,450 / 45,383)

Report-only conclusion: current-main baseline stale.

The newest successful coverage run tested 665c966, not current main dcf2925.

Coverage combines every CI test matrix job, including providers, CodeGen, .NET 8/10, Linux, Windows, and macOS, using canonical physical source and branch identities.

The comparison remains report-only while normal line and branch variance is calibrated.

Coverage details

Copilot AI review requested due to automatic review settings September 18, 2026 18:46

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Existing SQLite databases are not explicitly instructed to replace both stored query rows before restarting providers.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 1 Low severity

Open (1)

The provider supplies the common identity parameters `GrainIdHash`, `GrainIdN0`, `GrainIdN1`, `GrainTypeHash`, `GrainTypeString`, `GrainIdExtensionString`, and `ServiceId`. `WriteToStorageKey` also receives `GrainStateVersion` and `PayloadBinary`; `ClearStorageKey` and the optional `DeleteStorageKey` receive `GrainStateVersion`.

`ReadFromStorageKey` must return columns named `PayloadBinary` and `Version`. `WriteToStorageKey` must return the resulting version as `NewGrainStateVersion`. Clear and delete queries return one version value in their first result column; its name is ignored. A successful operation returns an advanced version, while an unchanged or missing value signals an ETag conflict. Versions must be representable as a signed 32-bit integer.
`ReadFromStorageKey` must return columns named `PayloadBinary` and `Version`. `WriteToStorageKey` must return the resulting version as `NewGrainStateVersion`. Clear and delete queries return one version value in their first result column; its name is ignored. A successful operation returns exactly one row across all result sets, containing the advanced version. An unchanged or missing value signals an ETag conflict. Versions must be representable as a signed 32-bit integer.
@ReubenBond
ReubenBond merged commit dcf2925 into dotnet:main Sep 18, 2026
67 checks passed
@ReubenBond
ReubenBond deleted the rb-11303-sqlite-persistence-version-rows branch September 18, 2026 18:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SQLite persistence: WriteToStorageKey/ClearStorageKey can return multiple NewGrainStateVersion rows, permanently breaking a grain's writes

2 participants