diff --git a/docs/documents/concurrency.md b/docs/documents/concurrency.md
index daf705ed7a..6239f57884 100644
--- a/docs/documents/concurrency.md
+++ b/docs/documents/concurrency.md
@@ -173,7 +173,7 @@ public class Reservation: IRevisioned
// other properties
- public long Version { get; set; }
+ public int Version { get; set; }
}
```
snippet source | anchor
diff --git a/docs/migration-guide.md b/docs/migration-guide.md
index 02b86723cd..65cf2e23d2 100644
--- a/docs/migration-guide.md
+++ b/docs/migration-guide.md
@@ -85,52 +85,52 @@ See [#4440](https://github.com/JasperFx/marten/issues/4440).
* See [#4306](https://github.com/JasperFx/marten/issues/4306).
-### Numeric document revisions widened from `int` to `long`
+### `IRevisioned` stays `int`; new `ILongVersioned` for 64-bit revisions
-* The numeric document revision (the value tracked in the `mt_version` column when `UseNumericRevisions` is enabled, or on aggregate documents that derive `Version` from the stream version) is now a 64-bit `long` everywhere. This removes the 2.1B-update-per-document ceiling that `int` imposed — comfortably within reach for high-throughput inline projections and per-event-snapshotted aggregates.
+::: warning Reversal since the early 9.0 alphas
+An early Marten 9 alpha widened `IRevisioned.Version` from `int` to `long`. **That was reverted before the 9.0 release candidate** (see [#4533](https://github.com/JasperFx/marten/pull/4533)). `IRevisioned.Version` is back to **`int`** — the Marten 8 signature. If you read an earlier alpha guide and already widened your `IRevisioned` documents to `long`, switch them to `ILongVersioned` (below) rather than leaving them on `IRevisioned`.
+:::
- **What changed on the .NET side:**
+* **`IRevisioned` is unchanged from Marten 8 — there is no migration to do for ordinary revisioned documents.** `IRevisioned.Version` is `int`. An ordinary per-document revision counter rarely approaches the `int` ceiling, so this is the right default.
- | Surface | Before | After |
+* **New: `ILongVersioned` (`long Version`).** Implement this instead of `IRevisioned` when the version is the global **event sequence number** — e.g. a document produced by a `MultiStreamProjection` — which can exceed `Int32.MaxValue`. A `MultiStreamProjection`-derived document that implements `IRevisioned` (int) overflows on the `bigint → int` read once its version passes `Int32`; `ILongVersioned` avoids that. Both interfaces opt the document into numeric revisioning and share the same `bigint` `mt_version` column — only the .NET member width differs. (See [#4526](https://github.com/JasperFx/marten/issues/4526) / [#4528](https://github.com/JasperFx/marten/issues/4528) and JasperFx [#348](https://github.com/JasperFx/jasperfx/issues/348).)
+
+ ```csharp
+ // Ordinary revisioned document — UNCHANGED from Marten 8 (int is correct, no edit needed)
+ public class Reservation : IRevisioned
+ {
+ public Guid Id { get; set; }
+ public int Version { get; set; }
+ }
+
+ // MultiStreamProjection document whose Version is the global event sequence number
+ public class CustomerSummary : ILongVersioned
+ {
+ public Guid Id { get; set; }
+ public long Version { get; set; } // long avoids Int32 overflow at high event counts
+ }
+ ```
+
+* **The `[Version]` attribute works on `int` or `long`.** A `[Version]`-annotated property used with `UseNumericRevisions` may be either type; no change is required, and you may use `long` if you need the wider range.
+
+* **The underlying column is `bigint`, and a few internal surfaces are `long`.** Independent of which interface you implement, the `mt_version` column is `bigint`, and Marten tracks the revision internally as a 64-bit value. The following are `long` (they were `int` in Marten 8):
+
+ | Surface | Marten 8 | Marten 9 |
| --- | --- | --- |
- | `Marten.Metadata.IRevisioned.Version` | `int` | `long` |
- | `[Version]`-annotated numeric properties on revisioned documents | `int` | `long` |
+ | `Marten.Metadata.IRevisioned.Version` | `int` | `int` (unchanged) |
+ | `JasperFx.ILongVersioned.Version` (new) | — | `long` |
| `DocumentMetadata.CurrentRevision` | `int` | `long` |
| `IDocumentSession.UpdateRevision(entity, revision)` parameter | `int` | `long` |
| `IDocumentSession.TryUpdateRevision(entity, revision)` parameter | `int` | `long` |
| `IRevisionedOperation.Revision` | `int` | `long` |
- | `MartenRegistry.MetadataConfig.Revision` | `Column` | `Column` |
-
- **What you have to change in your code:**
-
- * Any class implementing `IRevisioned`: widen `Version` to `long`.
-
- ```csharp
- // Before (Marten 8)
- public class Reservation : IRevisioned
- {
- public Guid Id { get; set; }
- public int Version { get; set; }
- }
+ | `MartenRegistry` metadata config `m.Revision` | `Column` | `Column` |
- // After (Marten 9)
- public class Reservation : IRevisioned
- {
- public Guid Id { get; set; }
- public long Version { get; set; }
- }
- ```
-
- * Any document with a `[Version]`-annotated numeric property used with `UseNumericRevisions`: widen the property to `long`.
- * Any `m.Revision.MapTo(x => x.SomeProperty)` configuration: the target property must be `long`. Mapping to an `int` property now throws `ArgumentOutOfRangeException` at mapping time.
- * Any code calling `IDocumentSession.UpdateRevision` / `TryUpdateRevision` with an explicit `int` literal compiles unchanged (`int` is implicitly convertible to `long`); explicit `int` locals passed in widen with a one-character edit.
+ These widenings are source-compatible for almost all callers: an `int` argument is implicitly convertible to `long`, so existing calls to `UpdateRevision` / `TryUpdateRevision` compile unchanged, and `m.Revision.MapTo(...)` accepts an `int` *or* `long` member. The only place you might touch code is a custom `IRevisionedOperation` implementation (a rare, advanced extensibility hook), where `Revision` is now `long`.
**Schema migration is automatic and non-destructive.** Existing Marten 8 deployments have an `integer` `mt_version` column. Marten 9's schema migration emits `ALTER TABLE … ALTER COLUMN mt_version TYPE bigint` and rewrites the associated `mt_upsert_*` / `mt_update_*` / `mt_overwrite_*` functions to accept and return `BIGINT`. All existing revision values are preserved — there is no data loss and no manual SQL to run.
**Bulk insert** of a revisioned document type pre-loads expected-version values as `bigint`. If you have a custom `IBulkLoader` implementation (rare), you must use `NpgsqlDbType.Bigint` instead of `NpgsqlDbType.Integer` for the expected-version column.
- See [#3733](https://github.com/JasperFx/marten/issues/3733) / [#4377](https://github.com/JasperFx/marten/pull/4377).
-
### Optional HSTORE-backed DCB tag storage
* Marten 9 adds an opt-in alternative storage layout for [DCB](events/dcb.md) tags: `DcbStorageMode.HStore`. The default (`DcbStorageMode.TagTables`) is unchanged, so **no migration is required** when upgrading from Marten 8 — existing tag tables and queries continue to work exactly as before.