Skip to content

fix(adonet): wrap grain state versions instead of overflowing - #11563

Merged
ReubenBond merged 6 commits into
dotnet:mainfrom
ScarletKuro:fix/adonet-persistence-version-overflow
Oct 28, 2026
Merged

ReubenBond merged 6 commits into
dotnet:mainfrom
ScarletKuro:fix/adonet-persistence-version-overflow

Conversation

@ScarletKuro

@ScarletKuro ScarletKuro commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #8379

ADO.NET grain storage increments a 32-bit integer version inside SQL. Once a row reaches 2,147,483,647, SQL Server, PostgreSQL and MySQL reject further writes. SQLite and Oracle can commit a larger value which the provider then cannot read as an integer.

The canonical persistence scripts now advance versions using Version % 2147483647 + 1 (MOD on Oracle), cycling from the maximum back to 1. Conditional mutations continue to match the supplied ETag, and a successful operation returns a different ETag. Keeping versions within the existing integer range preserves compatibility with older provider binaries and avoids rewriting the storage table to widen the column.

Database setup and upgrade

Each canonical *-Persistence.sql script supports setup and repeatable upgrades. New databases first run their shared Main script to create the query catalog, then the persistence script. Existing databases reapply the current persistence script: table/index setup preserves existing objects, routines are refreshed, and query keys are inserted or updated while grain payloads and unrelated queries are preserved. The new versioned migration copies and historical SQL test copies have been removed.

Stop all silos using the database before applying the persistence script, then restart them so every provider reloads its cached queries. Binary rolling upgrades can proceed after that database update. MySQL recreates procedures, so reapply any routine-specific EXECUTE grants before restarting. Oracle and SQLite also normalize already-committed versions above the integer maximum while preserving payloads. SQLite's _11_0 temporary-object names install the updated trigger on reused connections.

Accepted concurrency limit

An ETag repeats after 2,147,483,647 mutations of the same row. An activation retaining that ETag for a full cycle could then compare equal to the current version and overwrite newer state (ABA). At 1,000 mutations per second, the cycle takes about 25 days. The provider has no enforced maximum stale-ETag lifetime. The maintainer has accepted this bounded ABA limitation, which is also documented in the persistence README. DynamoDB also uses a finite counter, with a different cycle length.

Boundary and upgrade coverage

The shared provider scenario covers int.MaxValue - 1 → int.MaxValue → 1 → 2, stale write and clear rejection on both sides of the wrap, clear/delete at the maximum, and writing again after clear. It runs for SQL Server, PostgreSQL and MySQL with both clear modes, and SQLite.

Repeatable-upgrade tests install non-wrapping logic over the unchanged schema, populate grain state, reapply canonical SQL twice, and assert preservation of payloads, ETags and unrelated queries plus restoration of every persistence query. The boundary scenario also reads wrapped versions through a provider retaining its pre-upgrade cached read query. SQLite additionally covers oversized-version repair and replacement of a legacy temporary trigger on an existing connection. Oracle has no automated CI coverage.

Microsoft Reviewers: Open in CodeFlow

Copilot AI balanced review requested due to automatic review settings October 21, 2026 21:43

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 was unable to review this pull request because the user who requested the review has reached their quota limit.

@github-actions

github-actions Bot commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Code coverage

Metric Pull request Current main Variance
Lines 83.79% (120,784 / 144,155) 83.83% (120,850 / 144,155) -0.0458 pp
Branches 73.63% (36,243 / 49,222) 73.67% (36,263 / 49,222) -0.0406 pp

Report-only conclusion: regressed.

The current-main baseline is commit 30137dd94f and uses the same reviewed coverage matrix.

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

Preserve historical migrations and use current persistence scripts for fresh test databases. Require providers to reload cached SQL during upgrades and cover stale ETags and continued writes around the wrap boundary.
Copilot AI balanced review requested due to automatic review settings October 27, 2026 22:14

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 was unable to review this pull request because the user who requested the review has insufficient remaining quota.

Copilot AI balanced review requested due to automatic review settings October 27, 2026 23: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 was unable to review this pull request because the user who requested the review has insufficient remaining quota.

Use canonical provider SQL for setup and upgrades, refresh query catalogs without replacing grain state, and remove new versioned migration and historical fixture copies. Exercise repeated upgrades of populated databases and wrapped-version reads through pre-upgrade cached queries.
Copilot AI balanced review requested due to automatic review settings October 27, 2026 23:36

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 was unable to review this pull request because the user who requested the review has insufficient remaining quota.

@ReubenBond
ReubenBond merged commit 45e6acd into dotnet:main Oct 28, 2026
95 checks passed
@ScarletKuro
ScarletKuro deleted the fix/adonet-persistence-version-overflow branch October 28, 2026 20:05
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.

AdoNetGrainStorage throws SqlException because of Version column overflow

3 participants