From b51b41b1f3e0f74bf79fab7925938c36c2d20148 Mon Sep 17 00:00:00 2001 From: "Jeremy D. Miller" Date: Tue, 12 May 2026 12:30:24 -0500 Subject: [PATCH 1/2] =?UTF-8?q?Widen=20document=20Revision=20int=20?= =?UTF-8?q?=E2=86=92=20long=20across=20the=20revision=20metadata=20column?= =?UTF-8?q?=20(#3733)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Breaking change for Marten 9.0. The numeric revision tracking column (`mt_version` when `UseNumericRevisions` is on) is now `bigint` rather than `integer`, and all .NET-side surfaces that carry the revision widen from `int` to `long`: - `Marten.Metadata.IRevisioned.Version` - `DocumentMetadata.CurrentRevision` - `IRevisionedOperation.Revision` - `IDocumentSession.UpdateRevision` / `TryUpdateRevision` parameter - `IDocumentStorage.Store(IMartenSession, T, long)` - `VersionTracker.RevisionFor` / `StoreRevision` / `RevisionsFor` - `MetadataColumn` → `MetadataColumn` for `RevisionColumn` - Bulk-loader expected-version argument (`bigint`) - `mt_upsert_*` / `mt_update_*` / `mt_overwrite_*` function signatures and local variables (`final_version` / `current_version`) now `BIGINT` - `MartenRegistry.MetadataConfig.Revision` exposed as `Column` Schema migration from a Marten 8 deployment is non-destructive: the existing `integer` column is widened in place via `ALTER COLUMN ... TYPE bigint`, preserving all stored revision values. Switching from a Guid-versioned column to numeric revisions still drops and recreates the column. Also fixes an NRE in `MetadataColumn.Member.set` when the rejected member-type diagnostic ran on the first (null `_member`) assignment. User-visible code that implements `IRevisioned` or carries a `[Version]` numeric property must widen the property to `long`. The migration guide (#4355) covers this. Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/documents/concurrency.md | 6 ++--- ...verting_projection_from_inline_to_async.cs | 2 +- .../side_effects_in_aggregations.cs | 4 ++-- src/DaemonTests/TeleHealth/Appointments.cs | 2 +- src/DaemonTests/TeleHealth/ProviderShift.cs | 2 +- src/DaemonTests/TestingSupport/Trip.cs | 2 +- .../Bugs/Bug_3778_schema_name_issue.cs | 2 +- .../Concurrency/numeric_revisioning.cs | 6 ++--- .../RevisionedDocProvider1212098993.cs | 24 +++++++++---------- src/DocumentDbTests/Writing/bulk_loading.cs | 2 +- .../Aggregation/AggregationTestingSupport.cs | 2 +- .../Aggregation/OrderAggregate.cs | 2 +- .../Aggregation/stream_compacting.cs | 6 ++--- .../Aggregation/using_apply_metadata.cs | 2 +- .../when_finding_the_last_good_aggregation.cs | 8 +++---- ...10_inline_projections_with_quick_append.cs | 4 ++-- .../Examples/OptimizedCommandHandling.cs | 2 +- ...jection_metadata_for_inline_projections.cs | 2 +- .../fetching_live_aggregates_for_writing.cs | 8 +++---- ...with_event_member_identifier_end_to_end.cs | 2 +- ...on_with_stream_id_identifier_end_to_end.cs | 2 +- ...n_with_stream_key_identifier_end_to_end.cs | 2 +- .../Projections/testing_projections.cs | 2 +- ...sing_explicit_code_for_live_aggregation.cs | 4 ++-- src/EventSourcingTests/archiving_events.cs | 4 ++-- .../Examples/RevisionedDocuments.cs | 6 ++--- src/Marten/Events/EventDocumentStorage.cs | 2 +- src/Marten/Events/EventMapping.cs | 2 +- src/Marten/IDocumentOperations.cs | 4 ++-- .../CodeGeneration/BulkLoaderBuilder.cs | 4 ++-- .../DocumentSelectorWithDirtyChecking.cs | 2 +- .../DocumentSelectorWithIdentityMap.cs | 2 +- .../Internal/Operations/StorageOperation.cs | 10 ++++---- .../Internal/Sessions/DocumentSessionBase.cs | 4 ++-- .../Internal/Storage/DocumentStorage.cs | 2 +- .../Internal/Storage/IDocumentStorage.cs | 2 +- .../Storage/IdentityMapDocumentStorage.cs | 2 +- .../Storage/LightweightDocumentStorage.cs | 2 +- .../Storage/QueryOnlyDocumentStorage.cs | 2 +- .../Storage/SubClassDocumentStorage.cs | 2 +- .../ValueTypeIdentifiedDocumentStorage.cs | 2 +- src/Marten/Internal/VersionTracker.cs | 18 +++++++------- src/Marten/MartenRegistry.cs | 2 +- src/Marten/Metadata/IRevisioned.cs | 2 +- .../Arguments/ExpectedVersionArgument.cs | 10 ++++---- .../Schema/Arguments/RevisionArgument.cs | 8 +++---- .../Storage/Metadata/DocumentMetadata.cs | 2 +- src/Marten/Storage/Metadata/MetadataColumn.cs | 2 +- src/Marten/Storage/Metadata/RevisionColumn.cs | 17 +++++++++---- src/Marten/Storage/OverwriteFunction.cs | 6 ++--- src/Marten/Storage/UpdateFunction.cs | 6 ++--- src/Marten/Storage/UpsertFunction.cs | 6 ++--- .../marten_managed_tenant_id_partitioning.cs | 2 +- .../Patching/Bug_3261_patch_plus_revisions.cs | 2 +- 54 files changed, 123 insertions(+), 114 deletions(-) diff --git a/docs/documents/concurrency.md b/docs/documents/concurrency.md index bc5b0e8b8f..f24417d6b8 100644 --- a/docs/documents/concurrency.md +++ b/docs/documents/concurrency.md @@ -173,7 +173,7 @@ public class Reservation: IRevisioned // other properties - public int Version { get; set; } + public long Version { get; set; } } ``` snippet source | anchor @@ -200,11 +200,11 @@ public class Order { public Guid Id { get; set; } - // Marking an integer as the "version" + // Marking a long as the "version" // of the document, and making Marten // opt this document into the numeric revisioning [Version] - public int Version { get; set; } + public long Version { get; set; } } ``` snippet source | anchor diff --git a/src/DaemonTests/Aggregations/converting_projection_from_inline_to_async.cs b/src/DaemonTests/Aggregations/converting_projection_from_inline_to_async.cs index 9bf778cf29..845fa41e49 100644 --- a/src/DaemonTests/Aggregations/converting_projection_from_inline_to_async.cs +++ b/src/DaemonTests/Aggregations/converting_projection_from_inline_to_async.cs @@ -81,7 +81,7 @@ public async Task start_as_inline_move_to_async_and_just_continue() public class SimpleAggregate : IRevisioned { // This will be the aggregate version - public int Version { get; set; } + public long Version { get; set; } public Guid Id { get; set; } diff --git a/src/DaemonTests/Aggregations/side_effects_in_aggregations.cs b/src/DaemonTests/Aggregations/side_effects_in_aggregations.cs index 045cab109a..2469a4cf44 100644 --- a/src/DaemonTests/Aggregations/side_effects_in_aggregations.cs +++ b/src/DaemonTests/Aggregations/side_effects_in_aggregations.cs @@ -400,7 +400,7 @@ public class SideEffects1: IRevisioned public int B { get; set; } public int C { get; set; } public int D { get; set; } - public int Version { get; set; } + public long Version { get; set; } } public record WasDeleted(Guid Id); @@ -460,7 +460,7 @@ public class SideEffects2: IRevisioned public int B { get; set; } public int C { get; set; } public int D { get; set; } - public int Version { get; set; } + public long Version { get; set; } } public class RecordingMessageOutbox: IMessageOutbox diff --git a/src/DaemonTests/TeleHealth/Appointments.cs b/src/DaemonTests/TeleHealth/Appointments.cs index 178e8fffdf..9411410021 100644 --- a/src/DaemonTests/TeleHealth/Appointments.cs +++ b/src/DaemonTests/TeleHealth/Appointments.cs @@ -26,7 +26,7 @@ public class Appointment { public Guid Id { get; set; } - public int Version { get; set; } + public long Version { get; set; } public DateTimeOffset Created { get; set; } public string SpecialtyCode { get; set; } diff --git a/src/DaemonTests/TeleHealth/ProviderShift.cs b/src/DaemonTests/TeleHealth/ProviderShift.cs index 67bbfe0301..eb4545d8af 100644 --- a/src/DaemonTests/TeleHealth/ProviderShift.cs +++ b/src/DaemonTests/TeleHealth/ProviderShift.cs @@ -13,7 +13,7 @@ namespace DaemonTests.TeleHealth; public class ProviderShift(Guid boardId, Provider provider) { public Guid Id { get; set; } - public int Version { get; set; } + public long Version { get; set; } public Guid BoardId { get; private set; } = boardId; public Guid ProviderId => Provider.Id; public ProviderStatus Status { get; set; } = ProviderStatus.Paused; diff --git a/src/DaemonTests/TestingSupport/Trip.cs b/src/DaemonTests/TestingSupport/Trip.cs index d7c4e05a77..445f0e4106 100644 --- a/src/DaemonTests/TestingSupport/Trip.cs +++ b/src/DaemonTests/TestingSupport/Trip.cs @@ -44,7 +44,7 @@ public override string ToString() return $"{nameof(Id)}: {Id}, {nameof(EndedOn)}: {EndedOn}, {nameof(Traveled)}: {Traveled}, {nameof(State)}: {State}, {nameof(Active)}: {Active}, {nameof(StartedOn)}: {StartedOn}"; } - public int Version { get; set; } + public long Version { get; set; } } diff --git a/src/DocumentDbTests/Bugs/Bug_3778_schema_name_issue.cs b/src/DocumentDbTests/Bugs/Bug_3778_schema_name_issue.cs index c0f939d35b..4eef299087 100644 --- a/src/DocumentDbTests/Bugs/Bug_3778_schema_name_issue.cs +++ b/src/DocumentDbTests/Bugs/Bug_3778_schema_name_issue.cs @@ -45,4 +45,4 @@ public async Task TestSchemaNameEndingWith_d_In_Index(string schemaName) } public record User3778(Guid Id, string Name, DateTimeOffset D1, DateOnly D2, User3778 Manager, - DateTimeOffset LastModifiedOn, DateTimeOffset CreatedOn, int Version, bool IsArchived); + DateTimeOffset LastModifiedOn, DateTimeOffset CreatedOn, long Version, bool IsArchived); diff --git a/src/DocumentDbTests/Concurrency/numeric_revisioning.cs b/src/DocumentDbTests/Concurrency/numeric_revisioning.cs index d8f33fb81c..9eb1fb0d9f 100644 --- a/src/DocumentDbTests/Concurrency/numeric_revisioning.cs +++ b/src/DocumentDbTests/Concurrency/numeric_revisioning.cs @@ -575,7 +575,7 @@ public class RevisionedDoc: IRevisioned public Guid Id { get; set; } public string Name { get; set; } - public int Version { get; set; } + public long Version { get; set; } } public class OtherRevisionedDoc @@ -584,7 +584,7 @@ public class OtherRevisionedDoc public string Name { get; set; } [Version] - public int Version { get; set; } + public long Version { get; set; } } public class UnconventionallyVersionedDoc @@ -593,5 +593,5 @@ public class UnconventionallyVersionedDoc public string Name { get; set; } - public int UnconventionalVersion { get; set; } + public long UnconventionalVersion { get; set; } } diff --git a/src/DocumentDbTests/Internal/Generated/DocumentStorage/RevisionedDocProvider1212098993.cs b/src/DocumentDbTests/Internal/Generated/DocumentStorage/RevisionedDocProvider1212098993.cs index 0aec547d4f..96b1fc2a1a 100644 --- a/src/DocumentDbTests/Internal/Generated/DocumentStorage/RevisionedDocProvider1212098993.cs +++ b/src/DocumentDbTests/Internal/Generated/DocumentStorage/RevisionedDocProvider1212098993.cs @@ -237,7 +237,7 @@ public DocumentDbTests.Concurrency.RevisionedDoc Resolve(System.Data.Common.DbDa DocumentDbTests.Concurrency.RevisionedDoc document; document = _serializer.FromJson(reader, 0); - var version = reader.GetFieldValue(1); + var version = reader.GetFieldValue(1); document.Version = version; return document; } @@ -248,7 +248,7 @@ public DocumentDbTests.Concurrency.RevisionedDoc Resolve(System.Data.Common.DbDa DocumentDbTests.Concurrency.RevisionedDoc document; document = await _serializer.FromJsonAsync(reader, 0, token).ConfigureAwait(false); - var version = await reader.GetFieldValueAsync(1, token); + var version = await reader.GetFieldValueAsync(1, token); document.Version = version; return document; } @@ -279,7 +279,7 @@ public DocumentDbTests.Concurrency.RevisionedDoc Resolve(System.Data.Common.DbDa DocumentDbTests.Concurrency.RevisionedDoc document; document = _serializer.FromJson(reader, 1); - var version = reader.GetFieldValue(2); + var version = reader.GetFieldValue(2); document.Version = version; _session.MarkAsDocumentLoaded(id, document); return document; @@ -292,7 +292,7 @@ public DocumentDbTests.Concurrency.RevisionedDoc Resolve(System.Data.Common.DbDa DocumentDbTests.Concurrency.RevisionedDoc document; document = await _serializer.FromJsonAsync(reader, 1, token).ConfigureAwait(false); - var version = await reader.GetFieldValueAsync(2, token); + var version = await reader.GetFieldValueAsync(2, token); document.Version = version; _session.MarkAsDocumentLoaded(id, document); return document; @@ -325,7 +325,7 @@ public DocumentDbTests.Concurrency.RevisionedDoc Resolve(System.Data.Common.DbDa DocumentDbTests.Concurrency.RevisionedDoc document; document = _serializer.FromJson(reader, 1); - var version = reader.GetFieldValue(2); + var version = reader.GetFieldValue(2); document.Version = version; _session.MarkAsDocumentLoaded(id, document); _identityMap[id] = document; @@ -340,7 +340,7 @@ public DocumentDbTests.Concurrency.RevisionedDoc Resolve(System.Data.Common.DbDa DocumentDbTests.Concurrency.RevisionedDoc document; document = await _serializer.FromJsonAsync(reader, 1, token).ConfigureAwait(false); - var version = await reader.GetFieldValueAsync(2, token); + var version = await reader.GetFieldValueAsync(2, token); document.Version = version; _session.MarkAsDocumentLoaded(id, document); _identityMap[id] = document; @@ -374,7 +374,7 @@ public DocumentDbTests.Concurrency.RevisionedDoc Resolve(System.Data.Common.DbDa DocumentDbTests.Concurrency.RevisionedDoc document; document = _serializer.FromJson(reader, 1); - var version = reader.GetFieldValue(2); + var version = reader.GetFieldValue(2); document.Version = version; _session.MarkAsDocumentLoaded(id, document); _identityMap[id] = document; @@ -390,7 +390,7 @@ public DocumentDbTests.Concurrency.RevisionedDoc Resolve(System.Data.Common.DbDa DocumentDbTests.Concurrency.RevisionedDoc document; document = await _serializer.FromJsonAsync(reader, 1, token).ConfigureAwait(false); - var version = await reader.GetFieldValueAsync(2, token); + var version = await reader.GetFieldValueAsync(2, token); document.Version = version; _session.MarkAsDocumentLoaded(id, document); _identityMap[id] = document; @@ -1055,7 +1055,7 @@ public RevisionedDocBulkLoader1212098993(Marten.Internal.Storage.IDocumentStorag public const string OVERWRITE_WITH_VERSION_SQL = "update numeric_revisioning.mt_doc_revisioneddoc target SET data = source.data, mt_dotnet_type = source.mt_dotnet_type, mt_version = source.mt_version, mt_last_modified = transaction_timestamp() FROM mt_doc_revisioneddoc_temp source WHERE source.id = target.id and target.mt_version = source.mt_expected_version"; - public const string CREATE_TEMP_TABLE_FOR_COPYING_SQL = "create temporary table mt_doc_revisioneddoc_temp (like numeric_revisioning.mt_doc_revisioneddoc including defaults, \"mt_expected_version\" integer)"; + public const string CREATE_TEMP_TABLE_FOR_COPYING_SQL = "create temporary table mt_doc_revisioneddoc_temp (like numeric_revisioning.mt_doc_revisioneddoc including defaults, \"mt_expected_version\" bigint)"; public override string CreateTempTableForCopying() @@ -1086,7 +1086,7 @@ public override async System.Threading.Tasks.Task LoadRowAsync(Npgsql.NpgsqlBina { await writer.WriteAsync(document.GetType().FullName, NpgsqlTypes.NpgsqlDbType.Varchar, cancellation); await writer.WriteAsync(((DocumentDbTests.Concurrency.RevisionedDoc)document).Id, NpgsqlTypes.NpgsqlDbType.Uuid, cancellation); - await writer.WriteAsync(1, NpgsqlTypes.NpgsqlDbType.Integer, cancellation); + await writer.WriteAsync((long)1, NpgsqlTypes.NpgsqlDbType.Bigint, cancellation); await writer.WriteAsync(serializer.ToJson(document), NpgsqlTypes.NpgsqlDbType.Jsonb, cancellation); } @@ -1095,8 +1095,8 @@ public override async System.Threading.Tasks.Task LoadTempRowAsync(Npgsql.Npgsql { await writer.WriteAsync(document.GetType().FullName, NpgsqlTypes.NpgsqlDbType.Varchar, cancellation); await writer.WriteAsync(((DocumentDbTests.Concurrency.RevisionedDoc)document).Id, NpgsqlTypes.NpgsqlDbType.Uuid, cancellation); - writer.Write(document.Version <= 0 ? (object)System.DBNull.Value : (object)document.Version, NpgsqlTypes.NpgsqlDbType.Integer); - await writer.WriteAsync(1, NpgsqlTypes.NpgsqlDbType.Integer, cancellation); + writer.Write(document.Version <= 0 ? (object)System.DBNull.Value : (object)(long)document.Version, NpgsqlTypes.NpgsqlDbType.Bigint); + await writer.WriteAsync((long)1, NpgsqlTypes.NpgsqlDbType.Bigint, cancellation); await writer.WriteAsync(serializer.ToJson(document), NpgsqlTypes.NpgsqlDbType.Jsonb, cancellation); } diff --git a/src/DocumentDbTests/Writing/bulk_loading.cs b/src/DocumentDbTests/Writing/bulk_loading.cs index 65bdcb08cf..7c3853d9cb 100644 --- a/src/DocumentDbTests/Writing/bulk_loading.cs +++ b/src/DocumentDbTests/Writing/bulk_loading.cs @@ -707,6 +707,6 @@ public class RevisionedBulkDoc : IRevisioned { public Guid Id { get; set; } public string Name { get; set; } = string.Empty; - public int Version { get; set; } + public long Version { get; set; } } } diff --git a/src/EventSourcingTests/Aggregation/AggregationTestingSupport.cs b/src/EventSourcingTests/Aggregation/AggregationTestingSupport.cs index e29209c146..b17da88e9d 100644 --- a/src/EventSourcingTests/Aggregation/AggregationTestingSupport.cs +++ b/src/EventSourcingTests/Aggregation/AggregationTestingSupport.cs @@ -95,7 +95,7 @@ public class MyAggregate // This will be the aggregate version - public int Version { get; set; } + public long Version { get; set; } public Guid Id { get; set; } diff --git a/src/EventSourcingTests/Aggregation/OrderAggregate.cs b/src/EventSourcingTests/Aggregation/OrderAggregate.cs index 7f4b67ebce..608ba2322b 100644 --- a/src/EventSourcingTests/Aggregation/OrderAggregate.cs +++ b/src/EventSourcingTests/Aggregation/OrderAggregate.cs @@ -12,7 +12,7 @@ public class OrderAggregate // This would be set automatically by Marten if // used as the target of a SingleStreamAggregation - public int Version { get; set; } + public long Version { get; set; } public void Apply(OrderShipped shipped) => HasShipped = true; public bool HasShipped { get; private set; } diff --git a/src/EventSourcingTests/Aggregation/stream_compacting.cs b/src/EventSourcingTests/Aggregation/stream_compacting.cs index 04b75c8bad..ec7d98506f 100644 --- a/src/EventSourcingTests/Aggregation/stream_compacting.cs +++ b/src/EventSourcingTests/Aggregation/stream_compacting.cs @@ -361,7 +361,7 @@ public class Letters : IRevisioned public int BCount { get; set; } public int CCount { get; set; } public int DCount { get; set; } - public int Version { get; set; } + public long Version { get; set; } } public class LetterCounts: IRevisioned @@ -371,7 +371,7 @@ public class LetterCounts: IRevisioned public int BCount { get; set; } public int CCount { get; set; } public int DCount { get; set; } - public int Version { get; set; } + public long Version { get; set; } } public class LetterCountsByString: IRevisioned @@ -381,7 +381,7 @@ public class LetterCountsByString: IRevisioned public int BCount { get; set; } public int CCount { get; set; } public int DCount { get; set; } - public int Version { get; set; } + public long Version { get; set; } } public class LetterCountsByStringProjection: SingleStreamProjection diff --git a/src/EventSourcingTests/Aggregation/using_apply_metadata.cs b/src/EventSourcingTests/Aggregation/using_apply_metadata.cs index 41ebd8d80f..30a7617367 100644 --- a/src/EventSourcingTests/Aggregation/using_apply_metadata.cs +++ b/src/EventSourcingTests/Aggregation/using_apply_metadata.cs @@ -182,7 +182,7 @@ public class Item public string LastModifiedBy { get; set; } public DateTimeOffset? LastModified { get; set; } - public int Version { get; set; } + public long Version { get; set; } } public record ItemStarted(string Description); diff --git a/src/EventSourcingTests/Aggregation/when_finding_the_last_good_aggregation.cs b/src/EventSourcingTests/Aggregation/when_finding_the_last_good_aggregation.cs index b81e4f9759..ff8eeabe2f 100644 --- a/src/EventSourcingTests/Aggregation/when_finding_the_last_good_aggregation.cs +++ b/src/EventSourcingTests/Aggregation/when_finding_the_last_good_aggregation.cs @@ -82,10 +82,10 @@ public async Task finding_last_aggregate_using_string() public record DeleteYourself; -public class SimpleMaybeDeletedAggregate : IRevisioned +public class SimpleMaybeDeletedAggregate : Marten.Metadata.IRevisioned { // This will be the aggregate version - public int Version { get; set; } + public long Version { get; set; } public bool ShouldDelete(DeleteYourself _) => true; @@ -160,7 +160,7 @@ public override string ToString() } } -public class SimpleAsStringMaybeDeletedAggregate : IRevisioned +public class SimpleAsStringMaybeDeletedAggregate : Marten.Metadata.IRevisioned { protected bool Equals(SimpleAsStringMaybeDeletedAggregate other) { @@ -193,7 +193,7 @@ public override int GetHashCode() } // This will be the aggregate version - public int Version { get; set; } + public long Version { get; set; } public bool ShouldDelete(DeleteYourself _) => true; diff --git a/src/EventSourcingTests/Bugs/Bug_3310_inline_projections_with_quick_append.cs b/src/EventSourcingTests/Bugs/Bug_3310_inline_projections_with_quick_append.cs index dadfad4ff8..85de6a2862 100644 --- a/src/EventSourcingTests/Bugs/Bug_3310_inline_projections_with_quick_append.cs +++ b/src/EventSourcingTests/Bugs/Bug_3310_inline_projections_with_quick_append.cs @@ -213,7 +213,7 @@ public record LoadTestInlineProjection public Guid LastValue { get; init; } public long Sum { get; init; } [Version] - public int Version { get; set; } + public long Version { get; set; } public LoadTestInlineProjection Apply(LoadTestEvent @event, LoadTestInlineProjection current) { @@ -228,7 +228,7 @@ public record LoadTestUnrelatedInlineProjection public string StreamKey { get; init; } public long Count { get; init; } [Version] - public int Version { get; set; } + public long Version { get; set; } public LoadTestUnrelatedInlineProjection Apply(LoadTestUnrelatedEvent @event, LoadTestUnrelatedInlineProjection current) { diff --git a/src/EventSourcingTests/Examples/OptimizedCommandHandling.cs b/src/EventSourcingTests/Examples/OptimizedCommandHandling.cs index 8e345b9034..d0b3272639 100644 --- a/src/EventSourcingTests/Examples/OptimizedCommandHandling.cs +++ b/src/EventSourcingTests/Examples/OptimizedCommandHandling.cs @@ -36,7 +36,7 @@ public class Order // This is important, by Marten convention this would // be the - public int Version { get; set; } + public long Version { get; set; } public Order(OrderCreated created) { diff --git a/src/EventSourcingTests/FetchForWriting/fetch_for_writing_and_projection_metadata_for_inline_projections.cs b/src/EventSourcingTests/FetchForWriting/fetch_for_writing_and_projection_metadata_for_inline_projections.cs index 6ed265700c..af86ef0196 100644 --- a/src/EventSourcingTests/FetchForWriting/fetch_for_writing_and_projection_metadata_for_inline_projections.cs +++ b/src/EventSourcingTests/FetchForWriting/fetch_for_writing_and_projection_metadata_for_inline_projections.cs @@ -164,5 +164,5 @@ public class VersionedGuy public int CCount { get; set; } public int DCount { get; set; } - public int Version { get; set; } + public long Version { get; set; } } diff --git a/src/EventSourcingTests/FetchForWriting/fetching_live_aggregates_for_writing.cs b/src/EventSourcingTests/FetchForWriting/fetching_live_aggregates_for_writing.cs index 6ae40ff44d..adbacbd84e 100644 --- a/src/EventSourcingTests/FetchForWriting/fetching_live_aggregates_for_writing.cs +++ b/src/EventSourcingTests/FetchForWriting/fetching_live_aggregates_for_writing.cs @@ -776,7 +776,7 @@ public async Task work_correctly_for_multiple_calls_with_identity_map() public class SimpleAggregate : IRevisioned { // This will be the aggregate version - public int Version { get; set; } + public long Version { get; set; } public Guid Id { get; set; } @@ -848,7 +848,7 @@ public override int GetHashCode() public class SimpleAggregate2 { // This will be the aggregate version - public int Version { get; set; } + public long Version { get; set; } public Guid Id { get; set; } @@ -987,7 +987,7 @@ public class SomeProjection : IRevisioned public Guid Id { get; set; } public int A { get; set; } public void Apply(EventA e) => A++; - public int Version { get; set; } + public long Version { get; set; } } public class SomeOtherProjection : IRevisioned @@ -995,5 +995,5 @@ public class SomeOtherProjection : IRevisioned public Guid Id { get; set; } public int A { get; set; } public void Apply(EventA e) => A++; - public int Version { get; set; } + public long Version { get; set; } } diff --git a/src/EventSourcingTests/Projections/Flattened/flat_table_projection_with_event_member_identifier_end_to_end.cs b/src/EventSourcingTests/Projections/Flattened/flat_table_projection_with_event_member_identifier_end_to_end.cs index 02d6a184cf..76a532ed0f 100644 --- a/src/EventSourcingTests/Projections/Flattened/flat_table_projection_with_event_member_identifier_end_to_end.cs +++ b/src/EventSourcingTests/Projections/Flattened/flat_table_projection_with_event_member_identifier_end_to_end.cs @@ -52,7 +52,7 @@ public class Data public int C { get; set; } public int D { get; set; } public string Status { get; set; } - public int Version { get; set; } + public long Version { get; set; } public Guid Guid { get; set; } public DateTimeOffset Time { get; set; } } diff --git a/src/EventSourcingTests/Projections/Flattened/flat_table_projection_with_stream_id_identifier_end_to_end.cs b/src/EventSourcingTests/Projections/Flattened/flat_table_projection_with_stream_id_identifier_end_to_end.cs index f801d7fd08..497f111cb5 100644 --- a/src/EventSourcingTests/Projections/Flattened/flat_table_projection_with_stream_id_identifier_end_to_end.cs +++ b/src/EventSourcingTests/Projections/Flattened/flat_table_projection_with_stream_id_identifier_end_to_end.cs @@ -51,7 +51,7 @@ public class Data public int C { get; set; } public int D { get; set; } public string Status { get; set; } - public int Version { get; set; } + public long Version { get; set; } } private async Task readData(DbDataReader reader) diff --git a/src/EventSourcingTests/Projections/Flattened/flat_table_projection_with_stream_key_identifier_end_to_end.cs b/src/EventSourcingTests/Projections/Flattened/flat_table_projection_with_stream_key_identifier_end_to_end.cs index 13a1b3b9ea..2caa00111b 100644 --- a/src/EventSourcingTests/Projections/Flattened/flat_table_projection_with_stream_key_identifier_end_to_end.cs +++ b/src/EventSourcingTests/Projections/Flattened/flat_table_projection_with_stream_key_identifier_end_to_end.cs @@ -52,7 +52,7 @@ public class Data public int C { get; set; } public int D { get; set; } public string Status { get; set; } - public int Version { get; set; } + public long Version { get; set; } } private async Task readData(DbDataReader reader) diff --git a/src/EventSourcingTests/Projections/testing_projections.cs b/src/EventSourcingTests/Projections/testing_projections.cs index 4013d38e45..c3f8ce7181 100644 --- a/src/EventSourcingTests/Projections/testing_projections.cs +++ b/src/EventSourcingTests/Projections/testing_projections.cs @@ -58,7 +58,7 @@ public static Invoice Create(IEvent created) #endregion - public int Version { get; set; } + public long Version { get; set; } public decimal Amount { get; set; } public string Description { get; set; } diff --git a/src/EventSourcingTests/Projections/using_explicit_code_for_live_aggregation.cs b/src/EventSourcingTests/Projections/using_explicit_code_for_live_aggregation.cs index a750a7da6a..266d8e8e63 100644 --- a/src/EventSourcingTests/Projections/using_explicit_code_for_live_aggregation.cs +++ b/src/EventSourcingTests/Projections/using_explicit_code_for_live_aggregation.cs @@ -97,10 +97,10 @@ public async Task does_not_create_tables() #region sample_using_simple_explicit_code_for_live_aggregation -public class CountedAggregate: IRevisioned +public class CountedAggregate: Marten.Metadata.IRevisioned { // This will be the aggregate version - public int Version { get; set; } + public long Version { get; set; } public Guid Id { diff --git a/src/EventSourcingTests/archiving_events.cs b/src/EventSourcingTests/archiving_events.cs index bc5e1240b3..80349b1a5d 100644 --- a/src/EventSourcingTests/archiving_events.cs +++ b/src/EventSourcingTests/archiving_events.cs @@ -631,7 +631,7 @@ public record MaybeDeleted(bool ShouldDelete); public class SimpleAggregateStrongTypedGuid { // This will be the aggregate version - public int Version { get; set; } + public long Version { get; set; } public GuidId? Id { get; set; } @@ -674,7 +674,7 @@ public void Apply(EEvent _) public class SimpleAggregateStrongTypedString { // This will be the aggregate version - public int Version { get; set; } + public long Version { get; set; } public StringId? Id { get; set; } diff --git a/src/Marten.Testing/Examples/RevisionedDocuments.cs b/src/Marten.Testing/Examples/RevisionedDocuments.cs index 3d26a3175c..37166d21e1 100644 --- a/src/Marten.Testing/Examples/RevisionedDocuments.cs +++ b/src/Marten.Testing/Examples/RevisionedDocuments.cs @@ -71,11 +71,11 @@ public class Order { public Guid Id { get; set; } - // Marking an integer as the "version" + // Marking a long as the "version" // of the document, and making Marten // opt this document into the numeric revisioning [Version] - public int Version { get; set; } + public long Version { get; set; } } #endregion @@ -93,7 +93,7 @@ public class Reservation: IRevisioned // other properties - public int Version { get; set; } + public long Version { get; set; } } #endregion diff --git a/src/Marten/Events/EventDocumentStorage.cs b/src/Marten/Events/EventDocumentStorage.cs index 2404894eb8..b01d3ce5d9 100644 --- a/src/Marten/Events/EventDocumentStorage.cs +++ b/src/Marten/Events/EventDocumentStorage.cs @@ -193,7 +193,7 @@ public void Store(IMartenSession session, IEvent document, Guid? version) // Nothing } - public void Store(IMartenSession session, IEvent document, int revision) + public void Store(IMartenSession session, IEvent document, long revision) { // Nothing } diff --git a/src/Marten/Events/EventMapping.cs b/src/Marten/Events/EventMapping.cs index 3de068aa9f..c5ca6945d2 100644 --- a/src/Marten/Events/EventMapping.cs +++ b/src/Marten/Events/EventMapping.cs @@ -272,7 +272,7 @@ void IDocumentStorage.Store(IMartenSession session, T document, Guid? version throw new NotSupportedException(); } - public void Store(IMartenSession session, T document, int revision) + public void Store(IMartenSession session, T document, long revision) { throw new NotSupportedException(); } diff --git a/src/Marten/IDocumentOperations.cs b/src/Marten/IDocumentOperations.cs index 89850552d8..9bbc3abbb5 100644 --- a/src/Marten/IDocumentOperations.cs +++ b/src/Marten/IDocumentOperations.cs @@ -111,7 +111,7 @@ public interface IDocumentOperations: IQuerySession, IStorageOperations /// /// /// - void UpdateRevision(T entity, int revision) where T : notnull; + void UpdateRevision(T entity, long revision) where T : notnull; /// /// Explicitly marks a document as needing to be updated and supplies the @@ -122,7 +122,7 @@ public interface IDocumentOperations: IQuerySession, IStorageOperations /// /// /// - void TryUpdateRevision(T entity, int revision) where T : notnull; + void TryUpdateRevision(T entity, long revision) where T : notnull; /// /// Store an enumerable of potentially mixed documents diff --git a/src/Marten/Internal/CodeGeneration/BulkLoaderBuilder.cs b/src/Marten/Internal/CodeGeneration/BulkLoaderBuilder.cs index 101b435a2a..a4556c8d71 100644 --- a/src/Marten/Internal/CodeGeneration/BulkLoaderBuilder.cs +++ b/src/Marten/Internal/CodeGeneration/BulkLoaderBuilder.cs @@ -92,7 +92,7 @@ private List orderArgumentsForBulkWriting(UpsertFunction upsertF if (includeExpectedVersion && needsExpectedVersion()) { - var dbType = _mapping.UseNumericRevisions ? NpgsqlDbType.Integer : NpgsqlDbType.Uuid; + var dbType = _mapping.UseNumericRevisions ? NpgsqlDbType.Bigint : NpgsqlDbType.Uuid; var expectedArgument = new ExpectedVersionArgument(dbType); var insertIndex = arguments.FindIndex(x => x is VersionArgument || x is RevisionArgument); if (insertIndex < 0) @@ -184,7 +184,7 @@ public string CreateTempTableForCopying() return $"create temporary table {_tempTable} (like {_mapping.TableName.QualifiedName} including defaults)"; } - var expectedType = _mapping.UseNumericRevisions ? "integer" : "uuid"; + var expectedType = _mapping.UseNumericRevisions ? "bigint" : "uuid"; return $"create temporary table {_tempTable} (like {_mapping.TableName.QualifiedName} including defaults, \"{SchemaConstants.ExpectedVersionColumn}\" {expectedType})"; } diff --git a/src/Marten/Internal/CodeGeneration/DocumentSelectorWithDirtyChecking.cs b/src/Marten/Internal/CodeGeneration/DocumentSelectorWithDirtyChecking.cs index 39b2c29177..1480d155fa 100644 --- a/src/Marten/Internal/CodeGeneration/DocumentSelectorWithDirtyChecking.cs +++ b/src/Marten/Internal/CodeGeneration/DocumentSelectorWithDirtyChecking.cs @@ -45,7 +45,7 @@ public abstract class RevisionedDocumentSelectorWithDirtyChecking: IDocu protected readonly DocumentMapping _mapping; protected readonly ISerializer _serializer; protected readonly IMartenSession _session; - protected readonly Dictionary _versions; + protected readonly Dictionary _versions; public RevisionedDocumentSelectorWithDirtyChecking(IMartenSession session, DocumentMapping mapping) { diff --git a/src/Marten/Internal/CodeGeneration/DocumentSelectorWithIdentityMap.cs b/src/Marten/Internal/CodeGeneration/DocumentSelectorWithIdentityMap.cs index 55c727c0c1..42f9d1af44 100644 --- a/src/Marten/Internal/CodeGeneration/DocumentSelectorWithIdentityMap.cs +++ b/src/Marten/Internal/CodeGeneration/DocumentSelectorWithIdentityMap.cs @@ -34,7 +34,7 @@ public abstract class RevisionedDocumentSelectorWithIdentityMap: IDocume protected readonly Dictionary _identityMap; protected readonly DocumentMapping _mapping; protected readonly ISerializer _serializer; - protected readonly Dictionary _versions; + protected readonly Dictionary _versions; public RevisionedDocumentSelectorWithIdentityMap(IMartenSession session, DocumentMapping mapping) { diff --git a/src/Marten/Internal/Operations/StorageOperation.cs b/src/Marten/Internal/Operations/StorageOperation.cs index 5cf9a5f647..4296db1ada 100644 --- a/src/Marten/Internal/Operations/StorageOperation.cs +++ b/src/Marten/Internal/Operations/StorageOperation.cs @@ -19,7 +19,7 @@ namespace Marten.Internal.Operations; public interface IRevisionedOperation { - int Revision { get; set; } + long Revision { get; set; } bool IgnoreConcurrencyViolation { get; set; } } @@ -40,7 +40,7 @@ public StorageOperation(T document, TId id, Dictionary versions, Docu } // Using 0 as the default so that inserts "just work" - public int Revision { get; set; } = 0; + public long Revision { get; set; } = 0; public bool IgnoreConcurrencyViolation { get; set; } @@ -106,7 +106,7 @@ protected void setCurrentVersionParameter(IGroupedParameterBuilder builder) protected void setCurrentRevisionParameter(IGroupedParameterBuilder builder) { var parameter = builder.AppendParameter(Revision); - parameter.NpgsqlDbType = NpgsqlDbType.Integer; + parameter.NpgsqlDbType = NpgsqlDbType.Bigint; } protected bool postprocessConcurrency(DbDataReader reader, IList exceptions) @@ -130,7 +130,7 @@ protected bool postprocessRevision(DbDataReader reader, IList excepti var success = true; if (reader.Read()) { - var revision = reader.GetFieldValue(0); + var revision = reader.GetFieldValue(0); if (revision == 0) { exceptions.Add(new ConcurrencyException(typeof(T), _id)); @@ -162,7 +162,7 @@ protected async Task postprocessRevisionAsync(DbDataReader reader, IList(0, token).ConfigureAwait(false); + var revision = await reader.GetFieldValueAsync(0, token).ConfigureAwait(false); if (revision == 0) { exceptions.Add(new ConcurrencyException(typeof(T), _id)); diff --git a/src/Marten/Internal/Sessions/DocumentSessionBase.cs b/src/Marten/Internal/Sessions/DocumentSessionBase.cs index 8a9e334361..b4c44e42b0 100644 --- a/src/Marten/Internal/Sessions/DocumentSessionBase.cs +++ b/src/Marten/Internal/Sessions/DocumentSessionBase.cs @@ -89,7 +89,7 @@ public void UpdateExpectedVersion(T entity, Guid version) where T : notnull _workTracker.Add(op); } - public void UpdateRevision(T entity, int revision) where T : notnull + public void UpdateRevision(T entity, long revision) where T : notnull { assertNotDisposed(); @@ -104,7 +104,7 @@ public void UpdateRevision(T entity, int revision) where T : notnull _workTracker.Add(op); } - public void TryUpdateRevision(T entity, int revision) where T : notnull + public void TryUpdateRevision(T entity, long revision) where T : notnull { assertNotDisposed(); diff --git a/src/Marten/Internal/Storage/DocumentStorage.cs b/src/Marten/Internal/Storage/DocumentStorage.cs index 7f4a6e2745..72705b00ec 100644 --- a/src/Marten/Internal/Storage/DocumentStorage.cs +++ b/src/Marten/Internal/Storage/DocumentStorage.cs @@ -277,7 +277,7 @@ object IDocumentStorage.IdentityFor(T document) public abstract void Store(IMartenSession session, T document); public abstract void Store(IMartenSession session, T document, Guid? version); - public abstract void Store(IMartenSession session, T document, int revision); + public abstract void Store(IMartenSession session, T document, long revision); public abstract void Eject(IMartenSession session, T document); public abstract IStorageOperation Update(T document, IMartenSession session, string tenant); public abstract IStorageOperation Insert(T document, IMartenSession session, string tenant); diff --git a/src/Marten/Internal/Storage/IDocumentStorage.cs b/src/Marten/Internal/Storage/IDocumentStorage.cs index bbe2f93e6d..5ff11a383a 100644 --- a/src/Marten/Internal/Storage/IDocumentStorage.cs +++ b/src/Marten/Internal/Storage/IDocumentStorage.cs @@ -104,7 +104,7 @@ public interface IDocumentStorage: IDocumentStorage where T : notnull void Store(IMartenSession session, T document); void Store(IMartenSession session, T document, Guid? version); - void Store(IMartenSession session, T document, int revision); + void Store(IMartenSession session, T document, long revision); void Eject(IMartenSession session, T document); diff --git a/src/Marten/Internal/Storage/IdentityMapDocumentStorage.cs b/src/Marten/Internal/Storage/IdentityMapDocumentStorage.cs index fab357c509..0f4a042023 100644 --- a/src/Marten/Internal/Storage/IdentityMapDocumentStorage.cs +++ b/src/Marten/Internal/Storage/IdentityMapDocumentStorage.cs @@ -86,7 +86,7 @@ public sealed override void Store(IMartenSession session, T document, Guid? vers } } - public sealed override void Store(IMartenSession session, T document, int revision) + public sealed override void Store(IMartenSession session, T document, long revision) { store(session, document, out var id); diff --git a/src/Marten/Internal/Storage/LightweightDocumentStorage.cs b/src/Marten/Internal/Storage/LightweightDocumentStorage.cs index deaef8eec2..b0de2511fa 100644 --- a/src/Marten/Internal/Storage/LightweightDocumentStorage.cs +++ b/src/Marten/Internal/Storage/LightweightDocumentStorage.cs @@ -36,7 +36,7 @@ public sealed override void Store(IMartenSession session, T document, Guid? vers } } - public sealed override void Store(IMartenSession session, T document, int revision) + public sealed override void Store(IMartenSession session, T document, long revision) { var id = AssignIdentity(document, session.TenantId, session.Database); session.MarkAsAddedForStorage(id, document); diff --git a/src/Marten/Internal/Storage/QueryOnlyDocumentStorage.cs b/src/Marten/Internal/Storage/QueryOnlyDocumentStorage.cs index c0679f8979..418e77fe80 100644 --- a/src/Marten/Internal/Storage/QueryOnlyDocumentStorage.cs +++ b/src/Marten/Internal/Storage/QueryOnlyDocumentStorage.cs @@ -33,7 +33,7 @@ public sealed override void Store(IMartenSession session, T document, Guid? vers { } - public sealed override void Store(IMartenSession session, T document, int revision) + public sealed override void Store(IMartenSession session, T document, long revision) { } diff --git a/src/Marten/Internal/Storage/SubClassDocumentStorage.cs b/src/Marten/Internal/Storage/SubClassDocumentStorage.cs index 790b721397..8b414b7449 100644 --- a/src/Marten/Internal/Storage/SubClassDocumentStorage.cs +++ b/src/Marten/Internal/Storage/SubClassDocumentStorage.cs @@ -137,7 +137,7 @@ public void Store(IMartenSession session, T document, Guid? version) _parent.Store(session, document, version); } - public void Store(IMartenSession session, T document, int revision) + public void Store(IMartenSession session, T document, long revision) { _parent.Store(session, document, revision); } diff --git a/src/Marten/Internal/ValueTypeIdentifiedDocumentStorage.cs b/src/Marten/Internal/ValueTypeIdentifiedDocumentStorage.cs index dd79b67de1..27dc01d4bf 100644 --- a/src/Marten/Internal/ValueTypeIdentifiedDocumentStorage.cs +++ b/src/Marten/Internal/ValueTypeIdentifiedDocumentStorage.cs @@ -117,7 +117,7 @@ public ISqlFragment DefaultWhereFragment() public void Store(IMartenSession session, TDoc document, Guid? version) => Inner.Store(session, document, version); - public void Store(IMartenSession session, TDoc document, int revision) => Inner.Store(session, document, revision); + public void Store(IMartenSession session, TDoc document, long revision) => Inner.Store(session, document, revision); public void Eject(IMartenSession session, TDoc document) => Inner.Eject(session, document); diff --git a/src/Marten/Internal/VersionTracker.cs b/src/Marten/Internal/VersionTracker.cs index aadbf7c0da..9e25a724ef 100644 --- a/src/Marten/Internal/VersionTracker.cs +++ b/src/Marten/Internal/VersionTracker.cs @@ -8,11 +8,11 @@ public class VersionTracker { private readonly Dictionary _byType = new(); - public Dictionary RevisionsFor() where TId : notnull + public Dictionary RevisionsFor() where TId : notnull { if (_byType.TryGetValue(typeof(TDoc), out var item)) { - if (item is Dictionary d) + if (item is Dictionary d) { return d; } @@ -20,7 +20,7 @@ public Dictionary RevisionsFor() where TId : notnull throw new DocumentIdTypeMismatchException(typeof(TDoc), typeof(TId)); } - var dict = new Dictionary(); + var dict = new Dictionary(); _byType[typeof(TDoc)] = dict; return dict; @@ -62,11 +62,11 @@ public Dictionary ForType() where TId : notnull return null; } - public int? RevisionFor(TId id) where TId : notnull + public long? RevisionFor(TId id) where TId : notnull { if (_byType.TryGetValue(typeof(TDoc), out var item)) { - if (item is Dictionary dict) + if (item is Dictionary dict) { if (dict.TryGetValue(id, out var version)) { @@ -100,11 +100,11 @@ public void StoreVersion(TId id, Guid guid) where TId : notnull } } - public void StoreRevision(TId id, int revision) where TId : notnull + public void StoreRevision(TId id, long revision) where TId : notnull { if (_byType.TryGetValue(typeof(TDoc), out var item)) { - if (item is Dictionary d) + if (item is Dictionary d) { d[id] = revision; } @@ -115,7 +115,7 @@ public void StoreRevision(TId id, int revision) where TId : notnull } else { - var dict = new Dictionary { [id] = revision }; + var dict = new Dictionary { [id] = revision }; _byType.Add(typeof(TDoc), dict); } } @@ -135,7 +135,7 @@ public void ClearRevision(TId id) where TId : notnull { if (_byType.TryGetValue(typeof(TDoc), out var item)) { - if (item is Dictionary dict) + if (item is Dictionary dict) { dict.Remove(id); } diff --git a/src/Marten/MartenRegistry.cs b/src/Marten/MartenRegistry.cs index c045ed5f31..3422091905 100644 --- a/src/Marten/MartenRegistry.cs +++ b/src/Marten/MartenRegistry.cs @@ -913,7 +913,7 @@ public MetadataConfig(DocumentMappingExpression parent) /// /// The current numeric version of this document in the database /// - public Column Revision => new(_parent, m => m.Revision); + public Column Revision => new(_parent, m => m.Revision); /// /// Timestamp of the last time this document was modified diff --git a/src/Marten/Metadata/IRevisioned.cs b/src/Marten/Metadata/IRevisioned.cs index 753d832642..e28651f502 100644 --- a/src/Marten/Metadata/IRevisioned.cs +++ b/src/Marten/Metadata/IRevisioned.cs @@ -11,5 +11,5 @@ public interface IRevisioned /// /// Marten's version for this document /// - int Version { get; set; } + long Version { get; set; } } diff --git a/src/Marten/Schema/Arguments/ExpectedVersionArgument.cs b/src/Marten/Schema/Arguments/ExpectedVersionArgument.cs index fd48d0bb6c..9b83735573 100644 --- a/src/Marten/Schema/Arguments/ExpectedVersionArgument.cs +++ b/src/Marten/Schema/Arguments/ExpectedVersionArgument.cs @@ -14,7 +14,7 @@ public ExpectedVersionArgument(NpgsqlDbType dbType) Arg = "expected_version"; Column = SchemaConstants.ExpectedVersionColumn; DbType = dbType; - PostgresType = dbType == NpgsqlDbType.Integer ? "integer" : "uuid"; + PostgresType = dbType == NpgsqlDbType.Bigint ? "bigint" : "uuid"; } public override void GenerateBulkWriterCodeAsync(GeneratedType type, GeneratedMethod load, DocumentMapping mapping) @@ -25,7 +25,7 @@ public override void GenerateBulkWriterCodeAsync(GeneratedType type, GeneratedMe } else if (mapping.Metadata.Revision.Member != null) { - writeIntExpectedVersion(load, mapping.Metadata.Revision.Member); + writeLongExpectedVersion(load, mapping.Metadata.Revision.Member); } else { @@ -41,11 +41,11 @@ private void writeGuidExpectedVersion(GeneratedMethod load, MemberInfo member) $"writer.Write(document.{memberName} == Guid.Empty ? (object){typeof(DBNull).FullNameInCode()}.Value : (object)document.{memberName}, {dbTypeUsage});"); } - private void writeIntExpectedVersion(GeneratedMethod load, MemberInfo member) + private void writeLongExpectedVersion(GeneratedMethod load, MemberInfo member) { var memberName = member.Name; - var dbTypeUsage = Constant.ForEnum(NpgsqlDbType.Integer).Usage; + var dbTypeUsage = Constant.ForEnum(NpgsqlDbType.Bigint).Usage; load.Frames.Code( - $"writer.Write(document.{memberName} <= 0 ? (object){typeof(DBNull).FullNameInCode()}.Value : (object)document.{memberName}, {dbTypeUsage});"); + $"writer.Write(document.{memberName} <= 0 ? (object){typeof(DBNull).FullNameInCode()}.Value : (object)(long)document.{memberName}, {dbTypeUsage});"); } } diff --git a/src/Marten/Schema/Arguments/RevisionArgument.cs b/src/Marten/Schema/Arguments/RevisionArgument.cs index 491cd49f29..88bbbe2c41 100644 --- a/src/Marten/Schema/Arguments/RevisionArgument.cs +++ b/src/Marten/Schema/Arguments/RevisionArgument.cs @@ -11,8 +11,8 @@ internal class RevisionArgument: UpsertArgument public RevisionArgument() { Arg = "revision"; - PostgresType = "integer"; - DbType = NpgsqlDbType.Integer; + PostgresType = "bigint"; + DbType = NpgsqlDbType.Bigint; Column = SchemaConstants.VersionColumn; } @@ -26,7 +26,7 @@ public override void GenerateCodeToSetDbParameterValue(GeneratedMethod method, G public override void GenerateBulkWriterCodeAsync(GeneratedType type, GeneratedMethod load, DocumentMapping mapping) { load.Frames.CodeAsync( - "await writer.WriteAsync(1, {0}, {1});", - NpgsqlDbType.Integer, Use.Type()); + "await writer.WriteAsync((long)1, {0}, {1});", + NpgsqlDbType.Bigint, Use.Type()); } } diff --git a/src/Marten/Storage/Metadata/DocumentMetadata.cs b/src/Marten/Storage/Metadata/DocumentMetadata.cs index 7d530fadeb..974d26b017 100644 --- a/src/Marten/Storage/Metadata/DocumentMetadata.cs +++ b/src/Marten/Storage/Metadata/DocumentMetadata.cs @@ -28,7 +28,7 @@ public DocumentMetadata(object id) /// /// The current version of this document in the database if using numeric revisions /// - public int CurrentRevision { get; internal set; } + public long CurrentRevision { get; internal set; } /// /// Timestamp of the last time this document was modified diff --git a/src/Marten/Storage/Metadata/MetadataColumn.cs b/src/Marten/Storage/Metadata/MetadataColumn.cs index 2af89e196d..01870d9a73 100644 --- a/src/Marten/Storage/Metadata/MetadataColumn.cs +++ b/src/Marten/Storage/Metadata/MetadataColumn.cs @@ -122,7 +122,7 @@ public override MemberInfo Member if (value.GetRawMemberType() != typeof(T)) { throw new ArgumentOutOfRangeException(nameof(value), - $"The {_memberName} member of {_member.DeclaringType?.NameInCode() ?? "null"} has to be of type {typeof(T).NameInCode()}"); + $"The {_memberName} member of {value.DeclaringType?.NameInCode() ?? "null"} has to be of type {typeof(T).NameInCode()}"); } _member = value; diff --git a/src/Marten/Storage/Metadata/RevisionColumn.cs b/src/Marten/Storage/Metadata/RevisionColumn.cs index a2a9df28cc..017e27f886 100644 --- a/src/Marten/Storage/Metadata/RevisionColumn.cs +++ b/src/Marten/Storage/Metadata/RevisionColumn.cs @@ -9,7 +9,7 @@ namespace Marten.Storage.Metadata; -internal class RevisionColumn: MetadataColumn, ISelectableColumn +internal class RevisionColumn: MetadataColumn, ISelectableColumn { public RevisionColumn(): base(SchemaConstants.VersionColumn, x => x.CurrentRevision) { @@ -31,8 +31,8 @@ public void GenerateCode(StorageStyle storageStyle, GeneratedType generatedType, var versionPosition = index; //mapping.IsHierarchy() ? 3 : 2; async.Frames.CodeAsync( - $"var version = await reader.GetFieldValueAsync({versionPosition}, token);"); - sync.Frames.Code($"var version = reader.GetFieldValue({versionPosition});"); + $"var version = await reader.GetFieldValueAsync({versionPosition}, token);"); + sync.Frames.Code($"var version = reader.GetFieldValue({versionPosition});"); if (Member != null) { @@ -53,12 +53,21 @@ public bool ShouldSelect(DocumentMapping mapping, StorageStyle storageStyle) public override string AlterColumnTypeSql(Table table, TableColumn changeActual) { + // Non-destructive widening from the Marten 8 schema (integer) to Marten 9 (bigint). + // Existing revision values are preserved. + if (changeActual.Type.EqualsIgnoreCase("integer")) + { + return $"ALTER TABLE {table.Identifier.QualifiedName} ALTER COLUMN {Name} TYPE bigint;"; + } + + // Falling through here means the existing column is a Guid concurrency column (uuid) + // and the user is switching to numeric revisions: drop and recreate. return $"ALTER TABLE {table.Identifier.QualifiedName} DROP COLUMN {Name};{AddColumnSql(table)}"; } public override bool CanAlter(TableColumn actual) { - return actual.Type.EqualsIgnoreCase("uuid"); + return actual.Type.EqualsIgnoreCase("uuid") || actual.Type.EqualsIgnoreCase("integer"); } public override void WriteMetadataInUpdateStatement(ICommandBuilder builder, DocumentSessionBase session) diff --git a/src/Marten/Storage/OverwriteFunction.cs b/src/Marten/Storage/OverwriteFunction.cs index 0558669cd5..8010add3fc 100644 --- a/src/Marten/Storage/OverwriteFunction.cs +++ b/src/Marten/Storage/OverwriteFunction.cs @@ -16,12 +16,12 @@ protected override void writeFunction(TextWriter writer, string argList, string if (_mapping.Metadata.Revision.Enabled) { writer.WriteLine($@" -CREATE OR REPLACE FUNCTION {Identifier.QualifiedName}({argList}) RETURNS INTEGER LANGUAGE plpgsql { +CREATE OR REPLACE FUNCTION {Identifier.QualifiedName}({argList}) RETURNS BIGINT LANGUAGE plpgsql { securityDeclaration } AS $function$ DECLARE - final_version INTEGER; - current_version INTEGER; + final_version BIGINT; + current_version BIGINT; BEGIN if revision = 0 then diff --git a/src/Marten/Storage/UpdateFunction.cs b/src/Marten/Storage/UpdateFunction.cs index f23f019d17..100b50091b 100644 --- a/src/Marten/Storage/UpdateFunction.cs +++ b/src/Marten/Storage/UpdateFunction.cs @@ -21,12 +21,12 @@ protected override void writeFunction(TextWriter writer, string argList, string if (_mapping.Metadata.Revision.Enabled) { writer.WriteLine($@" -CREATE OR REPLACE FUNCTION {Identifier.QualifiedName}({argList}) RETURNS INTEGER LANGUAGE plpgsql { +CREATE OR REPLACE FUNCTION {Identifier.QualifiedName}({argList}) RETURNS BIGINT LANGUAGE plpgsql { securityDeclaration } AS $function$ DECLARE - final_version INTEGER; - current_version INTEGER; + final_version BIGINT; + current_version BIGINT; BEGIN if revision <= 1 then SELECT mt_version FROM {_tableName.QualifiedName} into current_version WHERE id = docId {_andTenantWhereClause}{_andPartitionWhereClause}; diff --git a/src/Marten/Storage/UpsertFunction.cs b/src/Marten/Storage/UpsertFunction.cs index 0a4176e626..8535bf49bc 100644 --- a/src/Marten/Storage/UpsertFunction.cs +++ b/src/Marten/Storage/UpsertFunction.cs @@ -216,12 +216,12 @@ protected virtual void writeFunction(TextWriter writer, string argList, string s if (_mapping.Metadata.Revision.Enabled) { writer.WriteLine($@" -CREATE OR REPLACE FUNCTION {Identifier.QualifiedName}({argList}) RETURNS INTEGER LANGUAGE plpgsql { +CREATE OR REPLACE FUNCTION {Identifier.QualifiedName}({argList}) RETURNS BIGINT LANGUAGE plpgsql { securityDeclaration } AS $function$ DECLARE - final_version INTEGER; - current_version INTEGER; + final_version BIGINT; + current_version BIGINT; BEGIN SELECT {_versionColumnName} into current_version FROM {_versionSourceTable} WHERE id = docId {_andTenantVersionWhereClause}{_andPartitionWhereClause}; diff --git a/src/MultiTenancyTests/marten_managed_tenant_id_partitioning.cs b/src/MultiTenancyTests/marten_managed_tenant_id_partitioning.cs index 7925780754..e9c41df1d5 100644 --- a/src/MultiTenancyTests/marten_managed_tenant_id_partitioning.cs +++ b/src/MultiTenancyTests/marten_managed_tenant_id_partitioning.cs @@ -311,7 +311,7 @@ public class DocThatShouldBeExempted2 public class SimpleAggregate : IRevisioned { // This will be the aggregate version - public int Version { get; set; } + public long Version { get; set; } public Guid Id { get; set; } diff --git a/src/PatchingTests/Patching/Bug_3261_patch_plus_revisions.cs b/src/PatchingTests/Patching/Bug_3261_patch_plus_revisions.cs index 0ed8a4e2fd..4f0a85cd1f 100644 --- a/src/PatchingTests/Patching/Bug_3261_patch_plus_revisions.cs +++ b/src/PatchingTests/Patching/Bug_3261_patch_plus_revisions.cs @@ -30,5 +30,5 @@ public class DocDto { public long Id { get; set; } public string Name { get; set; } - public int Version { get; set; } + public long Version { get; set; } } From 1f7e062d0380e05a5d1e8c7dcc4e3f7122ab2990 Mon Sep 17 00:00:00 2001 From: "Jeremy D. Miller" Date: Tue, 12 May 2026 14:29:08 -0500 Subject: [PATCH 2/2] Tighten [Version] attribute to long-only + widen missed test class (#3733 CI fix) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CI on #4377 surfaced one missed [Version]-annotated property: the test class MyAggregateWithDifferentVersionProperty had [Version] public int SpecialVersion { get; set; } The original sed-based widening only matched the literal "public int Version" property name; this one slipped through because the property is called SpecialVersion. Widened to long. Also tightened src/Marten/Schema/VersionAttribute.cs: * The Modify() branch that accepted int is removed — [Version] on a Marten 9 numeric-revisioning property must be long, since MetadataColumn rejects an int member at registration time. * A dedicated diagnostic now fires when a user annotates an int property, naming the declaring type/member and pointing at the migration-guide section so the fix is obvious from the stack trace. * The catch-all error message dropped "int" from the supported types list to match the new contract. Verified locally: * The previously-failing test EventSourcingTests.Aggregation.setting_version_number_on_aggregate.set_version_on_aggregate_with_explicit_Version_attribute passes. * Full EventSourcingTests on net9.0: 1274 passed, 2 skipped, 0 failed. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../Aggregation/setting_version_number_on_aggregate.cs | 2 +- src/Marten/Schema/VersionAttribute.cs | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/EventSourcingTests/Aggregation/setting_version_number_on_aggregate.cs b/src/EventSourcingTests/Aggregation/setting_version_number_on_aggregate.cs index 31ee37a998..5be57fe31c 100644 --- a/src/EventSourcingTests/Aggregation/setting_version_number_on_aggregate.cs +++ b/src/EventSourcingTests/Aggregation/setting_version_number_on_aggregate.cs @@ -132,7 +132,7 @@ public async Task set_version_on_aggregate_with_explicit_Version_attribute() public class MyAggregateWithDifferentVersionProperty { [Version] - public int SpecialVersion { get; set; } + public long SpecialVersion { get; set; } public Guid Id { get; set; } diff --git a/src/Marten/Schema/VersionAttribute.cs b/src/Marten/Schema/VersionAttribute.cs index be00083c96..86fef9a513 100644 --- a/src/Marten/Schema/VersionAttribute.cs +++ b/src/Marten/Schema/VersionAttribute.cs @@ -19,7 +19,7 @@ public override void Modify(DocumentMapping mapping, MemberInfo member) var memberType = member.GetMemberType(); if (memberType != typeof(Guid) && memberType != typeof(Guid?)) { - if (memberType == typeof(int) || memberType == typeof(long)) + if (memberType == typeof(long)) { mapping.UseNumericRevisions = true; mapping.Metadata.Revision.Enabled = true; @@ -29,8 +29,14 @@ public override void Modify(DocumentMapping mapping, MemberInfo member) return; } + if (memberType == typeof(int)) + { + throw new ArgumentOutOfRangeException(nameof(member), + $"The [Version] attribute on {member.DeclaringType?.FullName ?? "(unknown)"}.{member.Name} is annotated on an int. As of Marten 9, numeric document revisions are widened to long. Change the property type to long. See https://martendb.io/migration-guide.html#numeric-document-revisions-widened-from-int-to-long"); + } + throw new ArgumentOutOfRangeException(nameof(member), - "The [Version] attribute is only valid on properties or fields of type Guid/Guid for optimistic concurrency, or int/long for projected aggregate version"); + "The [Version] attribute is only valid on properties or fields of type Guid/Guid? for optimistic concurrency, or long for projected aggregate version"); } mapping.UseOptimisticConcurrency = true;