From ffd6f3e6a85ad6ea4a6a6d303e3edce88edef644 Mon Sep 17 00:00:00 2001 From: gliljas Date: Mon, 10 Apr 2023 23:43:45 +0200 Subject: [PATCH 1/5] Support multitenancy in id generators --- .../HiLoForcedTableSequenceTest.cs | 26 ++-- .../PooledForcedTableSequenceTest.cs | 22 +-- .../Async/IdGen/Enhanced/OptimizerTests.cs | 2 +- .../Enhanced/Sequence/HiLoSequenceTest.cs | 23 +-- .../Enhanced/Sequence/PooledSequenceTest.cs | 22 +-- .../Async/IdGen/Enhanced/SourceMock.cs | 2 +- .../IdGen/Enhanced/Table/HiLoTableTest.cs | 21 ++- .../IdGen/Enhanced/Table/PooledLoTableTest.cs | 17 +- .../IdGen/Enhanced/Table/PooledTableTest.cs | 18 ++- .../HiLoForcedTableSequenceTest.cs | 26 ++-- .../PooledForcedTableSequenceTest.cs | 22 +-- .../IdGen/Enhanced/OptimizerTests.cs | 4 +- .../Enhanced/Sequence/HiLoSequenceTest.cs | 23 +-- .../Enhanced/Sequence/PooledSequenceTest.cs | 22 +-- .../IdGen/Enhanced/SourceMock.cs | 13 +- .../IdGen/Enhanced/Table/HiLoTableTest.cs | 21 ++- .../IdGen/Enhanced/Table/PooledLoTableTest.cs | 17 +- .../IdGen/Enhanced/Table/PooledTableTest.cs | 18 ++- .../MultiTenancy/TestCaseWithMultiTenancy.cs | 60 +++++++ src/NHibernate.Test/TestCase.cs | 2 + .../Async/Id/Enhanced/IAccessCallback.cs | 2 +- .../Async/Id/Enhanced/OptimizerFactory.cs | 65 ++++---- .../Async/Id/Enhanced/TableGenerator.cs | 10 +- .../Async/Id/SequenceHiLoGenerator.cs | 11 +- src/NHibernate/Async/Id/TableGenerator.cs | 4 +- src/NHibernate/Async/Id/TableHiLoGenerator.cs | 11 +- src/NHibernate/Id/Enhanced/IAccessCallback.cs | 7 +- .../Id/Enhanced/OptimizerFactory.cs | 147 ++++++++++++------ .../Id/Enhanced/SequenceStructure.cs | 2 + src/NHibernate/Id/Enhanced/TableGenerator.cs | 20 +-- src/NHibernate/Id/Enhanced/TableStructure.cs | 2 + src/NHibernate/Id/SequenceHiLoGenerator.cs | 24 ++- src/NHibernate/Id/TableGenerator.cs | 4 +- src/NHibernate/Id/TableHiLoGenerator.cs | 65 +++++++- 34 files changed, 506 insertions(+), 249 deletions(-) create mode 100644 src/NHibernate.Test/MultiTenancy/TestCaseWithMultiTenancy.cs diff --git a/src/NHibernate.Test/Async/IdGen/Enhanced/Forcedtable/HiLoForcedTableSequenceTest.cs b/src/NHibernate.Test/Async/IdGen/Enhanced/Forcedtable/HiLoForcedTableSequenceTest.cs index 9aff73e4e83..abf9104e83f 100644 --- a/src/NHibernate.Test/Async/IdGen/Enhanced/Forcedtable/HiLoForcedTableSequenceTest.cs +++ b/src/NHibernate.Test/Async/IdGen/Enhanced/Forcedtable/HiLoForcedTableSequenceTest.cs @@ -8,16 +8,20 @@ //------------------------------------------------------------------------------ -using NUnit.Framework; using NHibernate.Id.Enhanced; -using System.Collections; +using NHibernate.Test.MultiTenancy; +using NUnit.Framework; namespace NHibernate.Test.IdGen.Enhanced.Forcedtable { using System.Threading.Tasks; - [TestFixture] - public class HiLoForcedTableSequenceTestAsync : TestCase + [TestFixture(null)] + [TestFixture("test")] + public class HiLoForcedTableSequenceTestAsync : TestCaseWithMultiTenancy { + public HiLoForcedTableSequenceTestAsync(string tenantIdentifier) : base(tenantIdentifier) + { + } protected override string[] Mappings { get { return new[] { "IdGen.Enhanced.Forcedtable.HiLo.hbm.xml" }; } @@ -54,9 +58,9 @@ public async Task TestNormalBoundaryAsync() long expectedId = i + 1; Assert.That(entities[i].Id, Is.EqualTo(expectedId)); Assert.That(generator.DatabaseStructure.TimesAccessed, Is.EqualTo(1)); // initialization - Assert.That(optimizer.LastSourceValue, Is.EqualTo(1)); // initialization - Assert.That(optimizer.LastValue, Is.EqualTo(i + 1)); - Assert.That(optimizer.HiValue, Is.EqualTo(increment + 1)); + Assert.That(optimizer.GetLastSourceValue(TenantIdentifier), Is.EqualTo(1)); // initialization + Assert.That(optimizer.GetLastValue(TenantIdentifier), Is.EqualTo(i + 1)); + Assert.That(optimizer.GetHiValue(TenantIdentifier), Is.EqualTo(increment + 1)); } // now force a "clock over" @@ -64,9 +68,9 @@ public async Task TestNormalBoundaryAsync() await (session.SaveAsync(entities[increment])); Assert.That(entities[optimizer.IncrementSize].Id, Is.EqualTo(optimizer.IncrementSize + 1)); Assert.That(generator.DatabaseStructure.TimesAccessed, Is.EqualTo(2)); // initialization + clock-over - Assert.That(optimizer.LastSourceValue, Is.EqualTo(2)); // initialization + clock-over - Assert.That(optimizer.LastValue, Is.EqualTo(increment + 1)); - Assert.That(optimizer.HiValue, Is.EqualTo(increment * 2 + 1)); + Assert.That(optimizer.GetLastSourceValue(TenantIdentifier), Is.EqualTo(2)); // initialization + clock-over + Assert.That(optimizer.GetLastValue(TenantIdentifier), Is.EqualTo(increment + 1)); + Assert.That(optimizer.GetHiValue(TenantIdentifier), Is.EqualTo(increment * 2 + 1)); await (transaction.CommitAsync()); } @@ -85,4 +89,4 @@ public async Task TestNormalBoundaryAsync() } } } -} \ No newline at end of file +} diff --git a/src/NHibernate.Test/Async/IdGen/Enhanced/Forcedtable/PooledForcedTableSequenceTest.cs b/src/NHibernate.Test/Async/IdGen/Enhanced/Forcedtable/PooledForcedTableSequenceTest.cs index f792da88124..9f5f3a4168a 100644 --- a/src/NHibernate.Test/Async/IdGen/Enhanced/Forcedtable/PooledForcedTableSequenceTest.cs +++ b/src/NHibernate.Test/Async/IdGen/Enhanced/Forcedtable/PooledForcedTableSequenceTest.cs @@ -8,16 +8,20 @@ //------------------------------------------------------------------------------ -using NUnit.Framework; using NHibernate.Id.Enhanced; -using System.Collections; +using NHibernate.Test.MultiTenancy; +using NUnit.Framework; namespace NHibernate.Test.IdGen.Enhanced.Forcedtable { using System.Threading.Tasks; - [TestFixture] - public class PooledForcedTableSequenceTestAsync : TestCase + [TestFixture(null)] + [TestFixture("test")] + public class PooledForcedTableSequenceTestAsync : TestCaseWithMultiTenancy { + public PooledForcedTableSequenceTestAsync(string tenantIdentifier) : base(tenantIdentifier) + { + } protected override string[] Mappings { get { return new[] { "IdGen.Enhanced.Forcedtable.Pooled.hbm.xml" }; } @@ -55,8 +59,8 @@ public async Task TestNormalBoundaryAsync() Assert.That(entities[i].Id, Is.EqualTo(expectedId)); // NOTE : initialization calls table twice Assert.That(generator.DatabaseStructure.TimesAccessed, Is.EqualTo(2)); // initialization - Assert.That(optimizer.LastSourceValue, Is.EqualTo(increment + 1)); // initialization - Assert.That(optimizer.LastValue, Is.EqualTo(i + 1)); + Assert.That(optimizer.GetLastSourceValue(TenantIdentifier), Is.EqualTo(increment + 1)); // initialization + Assert.That(optimizer.GetLastValue(TenantIdentifier), Is.EqualTo(i + 1)); } // now force a "clock over" @@ -66,8 +70,8 @@ public async Task TestNormalBoundaryAsync() Assert.That(entities[optimizer.IncrementSize].Id, Is.EqualTo(optimizer.IncrementSize + 1)); // initialization (2) + clock over Assert.That(generator.DatabaseStructure.TimesAccessed, Is.EqualTo(3)); - Assert.That(optimizer.LastSourceValue, Is.EqualTo(increment*2 + 1)); - Assert.That(optimizer.LastValue, Is.EqualTo(increment + 1)); + Assert.That(optimizer.GetLastSourceValue(TenantIdentifier), Is.EqualTo(increment*2 + 1)); + Assert.That(optimizer.GetLastValue(TenantIdentifier), Is.EqualTo(increment + 1)); await (transaction.CommitAsync()); } @@ -86,4 +90,4 @@ public async Task TestNormalBoundaryAsync() } } } -} \ No newline at end of file +} diff --git a/src/NHibernate.Test/Async/IdGen/Enhanced/OptimizerTests.cs b/src/NHibernate.Test/Async/IdGen/Enhanced/OptimizerTests.cs index e7e5c23fad0..a047e3aec35 100644 --- a/src/NHibernate.Test/Async/IdGen/Enhanced/OptimizerTests.cs +++ b/src/NHibernate.Test/Async/IdGen/Enhanced/OptimizerTests.cs @@ -8,8 +8,8 @@ //------------------------------------------------------------------------------ -using NUnit.Framework; using NHibernate.Id.Enhanced; +using NUnit.Framework; namespace NHibernate.Test.IdGen.Enhanced { diff --git a/src/NHibernate.Test/Async/IdGen/Enhanced/Sequence/HiLoSequenceTest.cs b/src/NHibernate.Test/Async/IdGen/Enhanced/Sequence/HiLoSequenceTest.cs index cb185e72c6f..4b1606cf330 100644 --- a/src/NHibernate.Test/Async/IdGen/Enhanced/Sequence/HiLoSequenceTest.cs +++ b/src/NHibernate.Test/Async/IdGen/Enhanced/Sequence/HiLoSequenceTest.cs @@ -11,13 +11,18 @@ using System.Collections; using NUnit.Framework; using NHibernate.Id.Enhanced; +using NHibernate.Test.MultiTenancy; namespace NHibernate.Test.IdGen.Enhanced.Sequence { using System.Threading.Tasks; - [TestFixture] - public class HiLoSequenceTestAsync : TestCase + [TestFixture(null)] + [TestFixture("test")] + public class HiLoSequenceTestAsync : TestCaseWithMultiTenancy { + public HiLoSequenceTestAsync(string tenantIdentifier) : base(tenantIdentifier) + { + } protected override string[] Mappings { get { return new[] { "IdGen.Enhanced.Sequence.HiLo.hbm.xml" }; } @@ -51,18 +56,18 @@ public async Task TestNormalBoundaryAsync() entities[i] = new Entity("" + (i + 1)); await (session.SaveAsync(entities[i])); Assert.That(generator.DatabaseStructure.TimesAccessed, Is.EqualTo(1)); // initialization - Assert.That(optimizer.LastSourceValue, Is.EqualTo(1)); // initialization - Assert.That(optimizer.LastValue, Is.EqualTo(i + 1)); - Assert.That(optimizer.HiValue, Is.EqualTo(increment + 1)); + Assert.That(optimizer.GetLastSourceValue(TenantIdentifier), Is.EqualTo(1)); // initialization + Assert.That(optimizer.GetLastValue(TenantIdentifier), Is.EqualTo(i + 1)); + Assert.That(optimizer.GetHiValue(TenantIdentifier), Is.EqualTo(increment + 1)); } // now force a "clock over" entities[increment] = new Entity("" + increment); await (session.SaveAsync(entities[increment])); Assert.That(generator.DatabaseStructure.TimesAccessed, Is.EqualTo(2)); - Assert.That(optimizer.LastSourceValue, Is.EqualTo(2)); - Assert.That(optimizer.LastValue, Is.EqualTo(increment + 1)); - Assert.That(optimizer.HiValue, Is.EqualTo(increment * 2 + 1)); + Assert.That(optimizer.GetLastSourceValue(TenantIdentifier), Is.EqualTo(2)); + Assert.That(optimizer.GetLastValue(TenantIdentifier), Is.EqualTo(increment + 1)); + Assert.That(optimizer.GetHiValue(TenantIdentifier), Is.EqualTo(increment * 2 + 1)); await (transaction.CommitAsync()); } @@ -80,4 +85,4 @@ public async Task TestNormalBoundaryAsync() } } } -} \ No newline at end of file +} diff --git a/src/NHibernate.Test/Async/IdGen/Enhanced/Sequence/PooledSequenceTest.cs b/src/NHibernate.Test/Async/IdGen/Enhanced/Sequence/PooledSequenceTest.cs index 7d0d4d11763..1d06ae11d89 100644 --- a/src/NHibernate.Test/Async/IdGen/Enhanced/Sequence/PooledSequenceTest.cs +++ b/src/NHibernate.Test/Async/IdGen/Enhanced/Sequence/PooledSequenceTest.cs @@ -8,16 +8,20 @@ //------------------------------------------------------------------------------ -using System.Collections; -using NUnit.Framework; using NHibernate.Id.Enhanced; +using NHibernate.Test.MultiTenancy; +using NUnit.Framework; namespace NHibernate.Test.IdGen.Enhanced.Sequence { using System.Threading.Tasks; - [TestFixture] - public class PooledSequenceTestAsync : TestCase + [TestFixture(null)] + [TestFixture("test")] + public class PooledSequenceTestAsync : TestCaseWithMultiTenancy { + public PooledSequenceTestAsync(string tenantIdentifier) : base(tenantIdentifier) + { + } protected override string[] Mappings { get { return new[] { "IdGen.Enhanced.Sequence.Pooled.hbm.xml" }; } @@ -51,16 +55,16 @@ public async Task TestNormalBoundaryAsync() entities[i] = new Entity("" + (i + 1)); await (session.SaveAsync(entities[i])); Assert.That(generator.DatabaseStructure.TimesAccessed, Is.EqualTo(2)); // initialization calls seq twice - Assert.That(optimizer.LastSourceValue, Is.EqualTo(increment + 1)); // initialization calls seq twice - Assert.That(optimizer.LastValue, Is.EqualTo(i + 1)); + Assert.That(optimizer.GetLastSourceValue(TenantIdentifier), Is.EqualTo(increment + 1)); // initialization calls seq twice + Assert.That(optimizer.GetLastValue(TenantIdentifier), Is.EqualTo(i + 1)); } // now force a "clock over" entities[increment] = new Entity("" + increment); await (session.SaveAsync(entities[increment])); Assert.That(generator.DatabaseStructure.TimesAccessed, Is.EqualTo(3)); // initialization (2) + clock over - Assert.That(optimizer.LastSourceValue, Is.EqualTo(increment * 2 + 1)); // initialization (2) + clock over - Assert.That(optimizer.LastValue, Is.EqualTo(increment + 1)); + Assert.That(optimizer.GetLastSourceValue(TenantIdentifier), Is.EqualTo(increment * 2 + 1)); // initialization (2) + clock over + Assert.That(optimizer.GetLastValue(TenantIdentifier), Is.EqualTo(increment + 1)); await (transaction.CommitAsync()); } @@ -79,4 +83,4 @@ public async Task TestNormalBoundaryAsync() } } } -} \ No newline at end of file +} diff --git a/src/NHibernate.Test/Async/IdGen/Enhanced/SourceMock.cs b/src/NHibernate.Test/Async/IdGen/Enhanced/SourceMock.cs index 0f9615a2ccd..85fc9520c87 100644 --- a/src/NHibernate.Test/Async/IdGen/Enhanced/SourceMock.cs +++ b/src/NHibernate.Test/Async/IdGen/Enhanced/SourceMock.cs @@ -46,4 +46,4 @@ public Task GetNextValueAsync(CancellationToken cancellationToken) } } } -} \ No newline at end of file +} diff --git a/src/NHibernate.Test/Async/IdGen/Enhanced/Table/HiLoTableTest.cs b/src/NHibernate.Test/Async/IdGen/Enhanced/Table/HiLoTableTest.cs index 962460f90e0..2e53c916aea 100644 --- a/src/NHibernate.Test/Async/IdGen/Enhanced/Table/HiLoTableTest.cs +++ b/src/NHibernate.Test/Async/IdGen/Enhanced/Table/HiLoTableTest.cs @@ -10,14 +10,19 @@ using System.Collections; using NHibernate.Id.Enhanced; +using NHibernate.Test.MultiTenancy; using NUnit.Framework; namespace NHibernate.Test.IdGen.Enhanced.Table { using System.Threading.Tasks; - [TestFixture] - public class HiLoTableTestAsync : TestCase + [TestFixture(null)] + [TestFixture("test")] + public class HiLoTableTestAsync : TestCaseWithMultiTenancy { + public HiLoTableTestAsync(string tenantIdentifier) : base(tenantIdentifier) + { + } protected override string[] Mappings { get { return new[] { "IdGen.Enhanced.Table.HiLo.hbm.xml" }; } @@ -50,18 +55,18 @@ public async Task TestNormalBoundaryAsync() entities[i] = new Entity("" + (i + 1)); await (s.SaveAsync(entities[i])); Assert.That(generator.TableAccessCount, Is.EqualTo(1)); // initialization - Assert.That(optimizer.LastSourceValue, Is.EqualTo(1)); // initialization - Assert.That(optimizer.LastValue, Is.EqualTo(i + 1)); - Assert.That(optimizer.HiValue, Is.EqualTo(increment + 1)); + Assert.That(optimizer.GetLastSourceValue(TenantIdentifier), Is.EqualTo(1)); // initialization + Assert.That(optimizer.GetLastValue(TenantIdentifier), Is.EqualTo(i + 1)); + Assert.That(optimizer.GetHiValue(TenantIdentifier), Is.EqualTo(increment + 1)); } // now force a "clock over" entities[increment] = new Entity("" + increment); await (s.SaveAsync(entities[increment])); Assert.That(generator.TableAccessCount, Is.EqualTo(2)); - Assert.That(optimizer.LastSourceValue, Is.EqualTo(2)); - Assert.That(optimizer.LastValue, Is.EqualTo(increment + 1)); - Assert.That(optimizer.HiValue, Is.EqualTo((increment * 2) + 1)); + Assert.That(optimizer.GetLastSourceValue(TenantIdentifier), Is.EqualTo(2)); + Assert.That(optimizer.GetLastValue(TenantIdentifier), Is.EqualTo(increment + 1)); + Assert.That(optimizer.GetHiValue(TenantIdentifier), Is.EqualTo((increment * 2) + 1)); await (transaction.CommitAsync()); } diff --git a/src/NHibernate.Test/Async/IdGen/Enhanced/Table/PooledLoTableTest.cs b/src/NHibernate.Test/Async/IdGen/Enhanced/Table/PooledLoTableTest.cs index d721443bdd6..af309699513 100644 --- a/src/NHibernate.Test/Async/IdGen/Enhanced/Table/PooledLoTableTest.cs +++ b/src/NHibernate.Test/Async/IdGen/Enhanced/Table/PooledLoTableTest.cs @@ -10,14 +10,19 @@ using System.Collections; using NHibernate.Id.Enhanced; +using NHibernate.Test.MultiTenancy; using NUnit.Framework; namespace NHibernate.Test.IdGen.Enhanced.Table { using System.Threading.Tasks; - [TestFixture] - public class PooledLoTableTestAsync : TestCase + [TestFixture(null)] + [TestFixture("test")] + public class PooledLoTableTestAsync : TestCaseWithMultiTenancy { + public PooledLoTableTestAsync(string tenantIdentifier) : base(tenantIdentifier) + { + } protected override string[] Mappings { get { return new[] { "IdGen.Enhanced.Table.PooledLo.hbm.xml" }; } @@ -50,16 +55,16 @@ public async Task TestNormalBoundaryAsync() entities[i] = new Entity("" + (i + 1)); await (s.SaveAsync(entities[i])); Assert.That(generator.TableAccessCount, Is.EqualTo(1)); // initialization - Assert.That(optimizer.LastSourceValue, Is.EqualTo(1)); // initialization - Assert.That(optimizer.LastValue, Is.EqualTo(i + 1)); + Assert.That(optimizer.GetLastSourceValue(TenantIdentifier), Is.EqualTo(1)); // initialization + Assert.That(optimizer.GetLastValue(TenantIdentifier), Is.EqualTo(i + 1)); } // now force a "clock over" entities[increment] = new Entity("" + increment); await (s.SaveAsync(entities[increment])); Assert.That(generator.TableAccessCount, Is.EqualTo(2)); - Assert.That(optimizer.LastSourceValue, Is.EqualTo(increment + 1)); - Assert.That(optimizer.LastValue, Is.EqualTo(increment + 1)); + Assert.That(optimizer.GetLastSourceValue(TenantIdentifier), Is.EqualTo(increment + 1)); + Assert.That(optimizer.GetLastValue(TenantIdentifier), Is.EqualTo(increment + 1)); await (transaction.CommitAsync()); } diff --git a/src/NHibernate.Test/Async/IdGen/Enhanced/Table/PooledTableTest.cs b/src/NHibernate.Test/Async/IdGen/Enhanced/Table/PooledTableTest.cs index 00c921da834..15be48dccaa 100644 --- a/src/NHibernate.Test/Async/IdGen/Enhanced/Table/PooledTableTest.cs +++ b/src/NHibernate.Test/Async/IdGen/Enhanced/Table/PooledTableTest.cs @@ -8,16 +8,20 @@ //------------------------------------------------------------------------------ -using System.Collections; using NHibernate.Id.Enhanced; +using NHibernate.Test.MultiTenancy; using NUnit.Framework; namespace NHibernate.Test.IdGen.Enhanced.Table { using System.Threading.Tasks; - [TestFixture] - public class PooledTableTestAsync : TestCase + [TestFixture(null)] + [TestFixture("test")] + public class PooledTableTestAsync : TestCaseWithMultiTenancy { + public PooledTableTestAsync(string tenantIdentifier) : base(tenantIdentifier) + { + } protected override string[] Mappings { get { return new[] { "IdGen.Enhanced.Table.Pooled.hbm.xml" }; } @@ -50,16 +54,16 @@ public async Task TestNormalBoundaryAsync() entities[i] = new Entity("" + (i + 1)); await (s.SaveAsync(entities[i])); Assert.That(generator.TableAccessCount, Is.EqualTo(2)); // initialization calls seq twice - Assert.That(optimizer.LastSourceValue, Is.EqualTo(increment + 1)); // initialization calls seq twice - Assert.That(optimizer.LastValue, Is.EqualTo(i + 1)); + Assert.That(optimizer.GetLastSourceValue(TenantIdentifier), Is.EqualTo(increment + 1)); // initialization calls seq twice + Assert.That(optimizer.GetLastValue(TenantIdentifier), Is.EqualTo(i + 1)); } // now force a "clock over" entities[increment] = new Entity("" + increment); await (s.SaveAsync(entities[increment])); Assert.That(generator.TableAccessCount, Is.EqualTo(3)); // initialization (2) + clock over - Assert.That(optimizer.LastSourceValue, Is.EqualTo((increment * 2) + 1)); // initialization (2) + clock over - Assert.That(optimizer.LastValue, Is.EqualTo(increment + 1)); + Assert.That(optimizer.GetLastSourceValue(TenantIdentifier), Is.EqualTo((increment * 2) + 1)); // initialization (2) + clock over + Assert.That(optimizer.GetLastValue(TenantIdentifier), Is.EqualTo(increment + 1)); await (transaction.CommitAsync()); } diff --git a/src/NHibernate.Test/IdGen/Enhanced/Forcedtable/HiLoForcedTableSequenceTest.cs b/src/NHibernate.Test/IdGen/Enhanced/Forcedtable/HiLoForcedTableSequenceTest.cs index c2ba2b1f589..e8647f3858f 100644 --- a/src/NHibernate.Test/IdGen/Enhanced/Forcedtable/HiLoForcedTableSequenceTest.cs +++ b/src/NHibernate.Test/IdGen/Enhanced/Forcedtable/HiLoForcedTableSequenceTest.cs @@ -1,12 +1,16 @@ -using NUnit.Framework; using NHibernate.Id.Enhanced; -using System.Collections; +using NHibernate.Test.MultiTenancy; +using NUnit.Framework; namespace NHibernate.Test.IdGen.Enhanced.Forcedtable { - [TestFixture] - public class HiLoForcedTableSequenceTest : TestCase + [TestFixture(null)] + [TestFixture("test")] + public class HiLoForcedTableSequenceTest : TestCaseWithMultiTenancy { + public HiLoForcedTableSequenceTest(string tenantIdentifier) : base(tenantIdentifier) + { + } protected override string[] Mappings { get { return new[] { "IdGen.Enhanced.Forcedtable.HiLo.hbm.xml" }; } @@ -43,9 +47,9 @@ public void TestNormalBoundary() long expectedId = i + 1; Assert.That(entities[i].Id, Is.EqualTo(expectedId)); Assert.That(generator.DatabaseStructure.TimesAccessed, Is.EqualTo(1)); // initialization - Assert.That(optimizer.LastSourceValue, Is.EqualTo(1)); // initialization - Assert.That(optimizer.LastValue, Is.EqualTo(i + 1)); - Assert.That(optimizer.HiValue, Is.EqualTo(increment + 1)); + Assert.That(optimizer.GetLastSourceValue(TenantIdentifier), Is.EqualTo(1)); // initialization + Assert.That(optimizer.GetLastValue(TenantIdentifier), Is.EqualTo(i + 1)); + Assert.That(optimizer.GetHiValue(TenantIdentifier), Is.EqualTo(increment + 1)); } // now force a "clock over" @@ -53,9 +57,9 @@ public void TestNormalBoundary() session.Save(entities[increment]); Assert.That(entities[optimizer.IncrementSize].Id, Is.EqualTo(optimizer.IncrementSize + 1)); Assert.That(generator.DatabaseStructure.TimesAccessed, Is.EqualTo(2)); // initialization + clock-over - Assert.That(optimizer.LastSourceValue, Is.EqualTo(2)); // initialization + clock-over - Assert.That(optimizer.LastValue, Is.EqualTo(increment + 1)); - Assert.That(optimizer.HiValue, Is.EqualTo(increment * 2 + 1)); + Assert.That(optimizer.GetLastSourceValue(TenantIdentifier), Is.EqualTo(2)); // initialization + clock-over + Assert.That(optimizer.GetLastValue(TenantIdentifier), Is.EqualTo(increment + 1)); + Assert.That(optimizer.GetHiValue(TenantIdentifier), Is.EqualTo(increment * 2 + 1)); transaction.Commit(); } @@ -74,4 +78,4 @@ public void TestNormalBoundary() } } } -} \ No newline at end of file +} diff --git a/src/NHibernate.Test/IdGen/Enhanced/Forcedtable/PooledForcedTableSequenceTest.cs b/src/NHibernate.Test/IdGen/Enhanced/Forcedtable/PooledForcedTableSequenceTest.cs index 91839abf07f..39598c9d591 100644 --- a/src/NHibernate.Test/IdGen/Enhanced/Forcedtable/PooledForcedTableSequenceTest.cs +++ b/src/NHibernate.Test/IdGen/Enhanced/Forcedtable/PooledForcedTableSequenceTest.cs @@ -1,12 +1,16 @@ -using NUnit.Framework; using NHibernate.Id.Enhanced; -using System.Collections; +using NHibernate.Test.MultiTenancy; +using NUnit.Framework; namespace NHibernate.Test.IdGen.Enhanced.Forcedtable { - [TestFixture] - public class PooledForcedTableSequenceTest : TestCase + [TestFixture(null)] + [TestFixture("test")] + public class PooledForcedTableSequenceTest : TestCaseWithMultiTenancy { + public PooledForcedTableSequenceTest(string tenantIdentifier) : base(tenantIdentifier) + { + } protected override string[] Mappings { get { return new[] { "IdGen.Enhanced.Forcedtable.Pooled.hbm.xml" }; } @@ -44,8 +48,8 @@ public void TestNormalBoundary() Assert.That(entities[i].Id, Is.EqualTo(expectedId)); // NOTE : initialization calls table twice Assert.That(generator.DatabaseStructure.TimesAccessed, Is.EqualTo(2)); // initialization - Assert.That(optimizer.LastSourceValue, Is.EqualTo(increment + 1)); // initialization - Assert.That(optimizer.LastValue, Is.EqualTo(i + 1)); + Assert.That(optimizer.GetLastSourceValue(TenantIdentifier), Is.EqualTo(increment + 1)); // initialization + Assert.That(optimizer.GetLastValue(TenantIdentifier), Is.EqualTo(i + 1)); } // now force a "clock over" @@ -55,8 +59,8 @@ public void TestNormalBoundary() Assert.That(entities[optimizer.IncrementSize].Id, Is.EqualTo(optimizer.IncrementSize + 1)); // initialization (2) + clock over Assert.That(generator.DatabaseStructure.TimesAccessed, Is.EqualTo(3)); - Assert.That(optimizer.LastSourceValue, Is.EqualTo(increment*2 + 1)); - Assert.That(optimizer.LastValue, Is.EqualTo(increment + 1)); + Assert.That(optimizer.GetLastSourceValue(TenantIdentifier), Is.EqualTo(increment*2 + 1)); + Assert.That(optimizer.GetLastValue(TenantIdentifier), Is.EqualTo(increment + 1)); transaction.Commit(); } @@ -75,4 +79,4 @@ public void TestNormalBoundary() } } } -} \ No newline at end of file +} diff --git a/src/NHibernate.Test/IdGen/Enhanced/OptimizerTests.cs b/src/NHibernate.Test/IdGen/Enhanced/OptimizerTests.cs index 8f0fd01986f..90842a1048e 100644 --- a/src/NHibernate.Test/IdGen/Enhanced/OptimizerTests.cs +++ b/src/NHibernate.Test/IdGen/Enhanced/OptimizerTests.cs @@ -1,5 +1,5 @@ -using NUnit.Framework; -using NHibernate.Id.Enhanced; +using NHibernate.Id.Enhanced; +using NUnit.Framework; namespace NHibernate.Test.IdGen.Enhanced { diff --git a/src/NHibernate.Test/IdGen/Enhanced/Sequence/HiLoSequenceTest.cs b/src/NHibernate.Test/IdGen/Enhanced/Sequence/HiLoSequenceTest.cs index 3cac164ec72..7cd6161bc02 100644 --- a/src/NHibernate.Test/IdGen/Enhanced/Sequence/HiLoSequenceTest.cs +++ b/src/NHibernate.Test/IdGen/Enhanced/Sequence/HiLoSequenceTest.cs @@ -1,12 +1,17 @@ using System.Collections; using NUnit.Framework; using NHibernate.Id.Enhanced; +using NHibernate.Test.MultiTenancy; namespace NHibernate.Test.IdGen.Enhanced.Sequence { - [TestFixture] - public class HiLoSequenceTest : TestCase + [TestFixture(null)] + [TestFixture("test")] + public class HiLoSequenceTest : TestCaseWithMultiTenancy { + public HiLoSequenceTest(string tenantIdentifier) : base(tenantIdentifier) + { + } protected override string[] Mappings { get { return new[] { "IdGen.Enhanced.Sequence.HiLo.hbm.xml" }; } @@ -40,18 +45,18 @@ public void TestNormalBoundary() entities[i] = new Entity("" + (i + 1)); session.Save(entities[i]); Assert.That(generator.DatabaseStructure.TimesAccessed, Is.EqualTo(1)); // initialization - Assert.That(optimizer.LastSourceValue, Is.EqualTo(1)); // initialization - Assert.That(optimizer.LastValue, Is.EqualTo(i + 1)); - Assert.That(optimizer.HiValue, Is.EqualTo(increment + 1)); + Assert.That(optimizer.GetLastSourceValue(TenantIdentifier), Is.EqualTo(1)); // initialization + Assert.That(optimizer.GetLastValue(TenantIdentifier), Is.EqualTo(i + 1)); + Assert.That(optimizer.GetHiValue(TenantIdentifier), Is.EqualTo(increment + 1)); } // now force a "clock over" entities[increment] = new Entity("" + increment); session.Save(entities[increment]); Assert.That(generator.DatabaseStructure.TimesAccessed, Is.EqualTo(2)); - Assert.That(optimizer.LastSourceValue, Is.EqualTo(2)); - Assert.That(optimizer.LastValue, Is.EqualTo(increment + 1)); - Assert.That(optimizer.HiValue, Is.EqualTo(increment * 2 + 1)); + Assert.That(optimizer.GetLastSourceValue(TenantIdentifier), Is.EqualTo(2)); + Assert.That(optimizer.GetLastValue(TenantIdentifier), Is.EqualTo(increment + 1)); + Assert.That(optimizer.GetHiValue(TenantIdentifier), Is.EqualTo(increment * 2 + 1)); transaction.Commit(); } @@ -69,4 +74,4 @@ public void TestNormalBoundary() } } } -} \ No newline at end of file +} diff --git a/src/NHibernate.Test/IdGen/Enhanced/Sequence/PooledSequenceTest.cs b/src/NHibernate.Test/IdGen/Enhanced/Sequence/PooledSequenceTest.cs index 3e73cc21a9d..b18c211b7ba 100644 --- a/src/NHibernate.Test/IdGen/Enhanced/Sequence/PooledSequenceTest.cs +++ b/src/NHibernate.Test/IdGen/Enhanced/Sequence/PooledSequenceTest.cs @@ -1,12 +1,16 @@ -using System.Collections; -using NUnit.Framework; using NHibernate.Id.Enhanced; +using NHibernate.Test.MultiTenancy; +using NUnit.Framework; namespace NHibernate.Test.IdGen.Enhanced.Sequence { - [TestFixture] - public class PooledSequenceTest : TestCase + [TestFixture(null)] + [TestFixture("test")] + public class PooledSequenceTest : TestCaseWithMultiTenancy { + public PooledSequenceTest(string tenantIdentifier) : base(tenantIdentifier) + { + } protected override string[] Mappings { get { return new[] { "IdGen.Enhanced.Sequence.Pooled.hbm.xml" }; } @@ -40,16 +44,16 @@ public void TestNormalBoundary() entities[i] = new Entity("" + (i + 1)); session.Save(entities[i]); Assert.That(generator.DatabaseStructure.TimesAccessed, Is.EqualTo(2)); // initialization calls seq twice - Assert.That(optimizer.LastSourceValue, Is.EqualTo(increment + 1)); // initialization calls seq twice - Assert.That(optimizer.LastValue, Is.EqualTo(i + 1)); + Assert.That(optimizer.GetLastSourceValue(TenantIdentifier), Is.EqualTo(increment + 1)); // initialization calls seq twice + Assert.That(optimizer.GetLastValue(TenantIdentifier), Is.EqualTo(i + 1)); } // now force a "clock over" entities[increment] = new Entity("" + increment); session.Save(entities[increment]); Assert.That(generator.DatabaseStructure.TimesAccessed, Is.EqualTo(3)); // initialization (2) + clock over - Assert.That(optimizer.LastSourceValue, Is.EqualTo(increment * 2 + 1)); // initialization (2) + clock over - Assert.That(optimizer.LastValue, Is.EqualTo(increment + 1)); + Assert.That(optimizer.GetLastSourceValue(TenantIdentifier), Is.EqualTo(increment * 2 + 1)); // initialization (2) + clock over + Assert.That(optimizer.GetLastValue(TenantIdentifier), Is.EqualTo(increment + 1)); transaction.Commit(); } @@ -68,4 +72,4 @@ public void TestNormalBoundary() } } } -} \ No newline at end of file +} diff --git a/src/NHibernate.Test/IdGen/Enhanced/SourceMock.cs b/src/NHibernate.Test/IdGen/Enhanced/SourceMock.cs index d7fcce2f9ec..3097d3f3814 100644 --- a/src/NHibernate.Test/IdGen/Enhanced/SourceMock.cs +++ b/src/NHibernate.Test/IdGen/Enhanced/SourceMock.cs @@ -6,15 +6,20 @@ public partial class SourceMock : IAccessCallback { private long _val; private readonly long _initialValue; + private readonly string _tenantId; private readonly int _increment; private int _timesCalled; public SourceMock(long initialValue) : this(initialValue, 1) { } - public SourceMock(long initialValue, int increment) : this(initialValue, increment, 0) { } + public SourceMock(long initialValue, int increment) : this(null, initialValue, increment, 0) { } - public SourceMock(long initialValue, int increment, int timesCalled) + public SourceMock(long initialValue, int increment, int timesCalled): this(null, initialValue, increment, timesCalled) { + } + public SourceMock(string tenantId, long initialValue, int increment, int timesCalled) + { + _tenantId = tenantId; _increment = increment; _timesCalled = timesCalled; @@ -57,6 +62,8 @@ private void InitValue() _val = _initialValue; } + public string GetTenantIdentifier() => _tenantId; + public int TimesCalled { get { return _timesCalled; } @@ -71,4 +78,4 @@ public long CurrentValue } } } -} \ No newline at end of file +} diff --git a/src/NHibernate.Test/IdGen/Enhanced/Table/HiLoTableTest.cs b/src/NHibernate.Test/IdGen/Enhanced/Table/HiLoTableTest.cs index 878252ce3dd..014788d6570 100644 --- a/src/NHibernate.Test/IdGen/Enhanced/Table/HiLoTableTest.cs +++ b/src/NHibernate.Test/IdGen/Enhanced/Table/HiLoTableTest.cs @@ -1,12 +1,17 @@ using System.Collections; using NHibernate.Id.Enhanced; +using NHibernate.Test.MultiTenancy; using NUnit.Framework; namespace NHibernate.Test.IdGen.Enhanced.Table { - [TestFixture] - public class HiLoTableTest : TestCase + [TestFixture(null)] + [TestFixture("test")] + public class HiLoTableTest : TestCaseWithMultiTenancy { + public HiLoTableTest(string tenantIdentifier) : base(tenantIdentifier) + { + } protected override string[] Mappings { get { return new[] { "IdGen.Enhanced.Table.HiLo.hbm.xml" }; } @@ -39,18 +44,18 @@ public void TestNormalBoundary() entities[i] = new Entity("" + (i + 1)); s.Save(entities[i]); Assert.That(generator.TableAccessCount, Is.EqualTo(1)); // initialization - Assert.That(optimizer.LastSourceValue, Is.EqualTo(1)); // initialization - Assert.That(optimizer.LastValue, Is.EqualTo(i + 1)); - Assert.That(optimizer.HiValue, Is.EqualTo(increment + 1)); + Assert.That(optimizer.GetLastSourceValue(TenantIdentifier), Is.EqualTo(1)); // initialization + Assert.That(optimizer.GetLastValue(TenantIdentifier), Is.EqualTo(i + 1)); + Assert.That(optimizer.GetHiValue(TenantIdentifier), Is.EqualTo(increment + 1)); } // now force a "clock over" entities[increment] = new Entity("" + increment); s.Save(entities[increment]); Assert.That(generator.TableAccessCount, Is.EqualTo(2)); - Assert.That(optimizer.LastSourceValue, Is.EqualTo(2)); - Assert.That(optimizer.LastValue, Is.EqualTo(increment + 1)); - Assert.That(optimizer.HiValue, Is.EqualTo((increment * 2) + 1)); + Assert.That(optimizer.GetLastSourceValue(TenantIdentifier), Is.EqualTo(2)); + Assert.That(optimizer.GetLastValue(TenantIdentifier), Is.EqualTo(increment + 1)); + Assert.That(optimizer.GetHiValue(TenantIdentifier), Is.EqualTo((increment * 2) + 1)); transaction.Commit(); } diff --git a/src/NHibernate.Test/IdGen/Enhanced/Table/PooledLoTableTest.cs b/src/NHibernate.Test/IdGen/Enhanced/Table/PooledLoTableTest.cs index bd5245dbe9a..e56c2fed120 100644 --- a/src/NHibernate.Test/IdGen/Enhanced/Table/PooledLoTableTest.cs +++ b/src/NHibernate.Test/IdGen/Enhanced/Table/PooledLoTableTest.cs @@ -1,12 +1,17 @@ using System.Collections; using NHibernate.Id.Enhanced; +using NHibernate.Test.MultiTenancy; using NUnit.Framework; namespace NHibernate.Test.IdGen.Enhanced.Table { - [TestFixture] - public class PooledLoTableTest : TestCase + [TestFixture(null)] + [TestFixture("test")] + public class PooledLoTableTest : TestCaseWithMultiTenancy { + public PooledLoTableTest(string tenantIdentifier) : base(tenantIdentifier) + { + } protected override string[] Mappings { get { return new[] { "IdGen.Enhanced.Table.PooledLo.hbm.xml" }; } @@ -39,16 +44,16 @@ public void TestNormalBoundary() entities[i] = new Entity("" + (i + 1)); s.Save(entities[i]); Assert.That(generator.TableAccessCount, Is.EqualTo(1)); // initialization - Assert.That(optimizer.LastSourceValue, Is.EqualTo(1)); // initialization - Assert.That(optimizer.LastValue, Is.EqualTo(i + 1)); + Assert.That(optimizer.GetLastSourceValue(TenantIdentifier), Is.EqualTo(1)); // initialization + Assert.That(optimizer.GetLastValue(TenantIdentifier), Is.EqualTo(i + 1)); } // now force a "clock over" entities[increment] = new Entity("" + increment); s.Save(entities[increment]); Assert.That(generator.TableAccessCount, Is.EqualTo(2)); - Assert.That(optimizer.LastSourceValue, Is.EqualTo(increment + 1)); - Assert.That(optimizer.LastValue, Is.EqualTo(increment + 1)); + Assert.That(optimizer.GetLastSourceValue(TenantIdentifier), Is.EqualTo(increment + 1)); + Assert.That(optimizer.GetLastValue(TenantIdentifier), Is.EqualTo(increment + 1)); transaction.Commit(); } diff --git a/src/NHibernate.Test/IdGen/Enhanced/Table/PooledTableTest.cs b/src/NHibernate.Test/IdGen/Enhanced/Table/PooledTableTest.cs index 3a7ca042514..4f357bc6066 100644 --- a/src/NHibernate.Test/IdGen/Enhanced/Table/PooledTableTest.cs +++ b/src/NHibernate.Test/IdGen/Enhanced/Table/PooledTableTest.cs @@ -1,12 +1,16 @@ -using System.Collections; using NHibernate.Id.Enhanced; +using NHibernate.Test.MultiTenancy; using NUnit.Framework; namespace NHibernate.Test.IdGen.Enhanced.Table { - [TestFixture] - public class PooledTableTest : TestCase + [TestFixture(null)] + [TestFixture("test")] + public class PooledTableTest : TestCaseWithMultiTenancy { + public PooledTableTest(string tenantIdentifier) : base(tenantIdentifier) + { + } protected override string[] Mappings { get { return new[] { "IdGen.Enhanced.Table.Pooled.hbm.xml" }; } @@ -39,16 +43,16 @@ public void TestNormalBoundary() entities[i] = new Entity("" + (i + 1)); s.Save(entities[i]); Assert.That(generator.TableAccessCount, Is.EqualTo(2)); // initialization calls seq twice - Assert.That(optimizer.LastSourceValue, Is.EqualTo(increment + 1)); // initialization calls seq twice - Assert.That(optimizer.LastValue, Is.EqualTo(i + 1)); + Assert.That(optimizer.GetLastSourceValue(TenantIdentifier), Is.EqualTo(increment + 1)); // initialization calls seq twice + Assert.That(optimizer.GetLastValue(TenantIdentifier), Is.EqualTo(i + 1)); } // now force a "clock over" entities[increment] = new Entity("" + increment); s.Save(entities[increment]); Assert.That(generator.TableAccessCount, Is.EqualTo(3)); // initialization (2) + clock over - Assert.That(optimizer.LastSourceValue, Is.EqualTo((increment * 2) + 1)); // initialization (2) + clock over - Assert.That(optimizer.LastValue, Is.EqualTo(increment + 1)); + Assert.That(optimizer.GetLastSourceValue(TenantIdentifier), Is.EqualTo((increment * 2) + 1)); // initialization (2) + clock over + Assert.That(optimizer.GetLastValue(TenantIdentifier), Is.EqualTo(increment + 1)); transaction.Commit(); } diff --git a/src/NHibernate.Test/MultiTenancy/TestCaseWithMultiTenancy.cs b/src/NHibernate.Test/MultiTenancy/TestCaseWithMultiTenancy.cs new file mode 100644 index 00000000000..5ce6ae74477 --- /dev/null +++ b/src/NHibernate.Test/MultiTenancy/TestCaseWithMultiTenancy.cs @@ -0,0 +1,60 @@ +using System.Data.Common; +using NHibernate.Cfg; +using NHibernate.Dialect; +using NHibernate.Driver; +using NHibernate.MultiTenancy; + +namespace NHibernate.Test.MultiTenancy +{ + public abstract class TestCaseWithMultiTenancy : TestCase + { + protected TestCaseWithMultiTenancy(string tenantIdentifier) + { + TenantIdentifier = tenantIdentifier; + } + + public string TenantIdentifier { get; } + + protected override void Configure(Configuration configuration) + { + if (TenantIdentifier != null) + { + configuration.DataBaseIntegration( + x => + { + x.MultiTenancy = MultiTenancyStrategy.Database; + x.MultiTenancyConnectionProvider(); + }); + } + base.Configure(configuration); + } + + protected override DbConnection OpenConnectionForSchemaExport() + { + if (TenantIdentifier != null) + { + return Sfi.Settings.MultiTenancyConnectionProvider + .GetConnectionAccess(GetTenantConfig("defaultTenant"), Sfi).GetConnection(); + } + return base.OpenConnectionForSchemaExport(); + } + + private TenantConfiguration GetTenantConfig(string tenantId) + { + return new TestTenantConfiguration(tenantId, IsSqlServerDialect); + } + + protected override ISession OpenSession() + { + if (TenantIdentifier != null) + { + return Sfi.WithOptions().Tenant(new TestTenantConfiguration(TenantIdentifier, IsSqlServerDialect)).OpenSession(); + } + return base.OpenSession(); + } + + //Create extension method for this? + private bool IsSqlServerDialect => Sfi.Dialect is MsSql2000Dialect && !(Sfi.ConnectionProvider.Driver is OdbcDriver); + + } +} diff --git a/src/NHibernate.Test/TestCase.cs b/src/NHibernate.Test/TestCase.cs index 1335b6a722a..5db87e14a72 100644 --- a/src/NHibernate.Test/TestCase.cs +++ b/src/NHibernate.Test/TestCase.cs @@ -21,6 +21,8 @@ using NHibernate.SqlTypes; using NHibernate.Util; using NSubstitute; +using NHibernate.MultiTenancy; +using NHibernate.Test.MultiTenancy; namespace NHibernate.Test { diff --git a/src/NHibernate/Async/Id/Enhanced/IAccessCallback.cs b/src/NHibernate/Async/Id/Enhanced/IAccessCallback.cs index b52c3e524b5..8fbb5c27826 100644 --- a/src/NHibernate/Async/Id/Enhanced/IAccessCallback.cs +++ b/src/NHibernate/Async/Id/Enhanced/IAccessCallback.cs @@ -20,4 +20,4 @@ public partial interface IAccessCallback /// A cancellation token that can be used to cancel the work Task GetNextValueAsync(CancellationToken cancellationToken); } -} \ No newline at end of file +} diff --git a/src/NHibernate/Async/Id/Enhanced/OptimizerFactory.cs b/src/NHibernate/Async/Id/Enhanced/OptimizerFactory.cs index 5ba08f4a31e..84f66d725ce 100644 --- a/src/NHibernate/Async/Id/Enhanced/OptimizerFactory.cs +++ b/src/NHibernate/Async/Id/Enhanced/OptimizerFactory.cs @@ -9,8 +9,8 @@ using System; +using System.Collections.Concurrent; using System.Reflection; -using System.Runtime.CompilerServices; using NHibernate.Util; namespace NHibernate.Id.Enhanced @@ -30,27 +30,29 @@ public override async Task GenerateAsync(IAccessCallback callback, Cance cancellationToken.ThrowIfCancellationRequested(); using (await (_asyncLock.LockAsync()).ConfigureAwait(false)) { - if (_lastSourceValue < 0) + var generationState = _stateStore.LocateGenerationState(callback.GetTenantIdentifier()); + + if (generationState.LastSourceValue < 0) { - _lastSourceValue = await (callback.GetNextValueAsync(cancellationToken)).ConfigureAwait(false); - while (_lastSourceValue <= 0) + generationState.LastSourceValue = await (callback.GetNextValueAsync(cancellationToken)).ConfigureAwait(false); + while (generationState.LastSourceValue <= 0) { - _lastSourceValue = await (callback.GetNextValueAsync(cancellationToken)).ConfigureAwait(false); + generationState.LastSourceValue = await (callback.GetNextValueAsync(cancellationToken)).ConfigureAwait(false); } // upperLimit defines the upper end of the bucket values - _upperLimit = (_lastSourceValue * IncrementSize) + 1; + generationState.UpperLimit = (generationState.LastSourceValue * IncrementSize) + 1; // initialize value to the low end of the bucket - _value = _upperLimit - IncrementSize; + generationState.Value = generationState.UpperLimit - IncrementSize; } - else if (_upperLimit <= _value) + else if (generationState.UpperLimit <= generationState.Value) { - _lastSourceValue = await (callback.GetNextValueAsync(cancellationToken)).ConfigureAwait(false); - _upperLimit = (_lastSourceValue * IncrementSize) + 1; + generationState.LastSourceValue = await (callback.GetNextValueAsync(cancellationToken)).ConfigureAwait(false); + generationState.UpperLimit = (generationState.LastSourceValue * IncrementSize) + 1; } - return Make(_value++); + return Make(generationState.Value++); } } } @@ -106,37 +108,38 @@ public override async Task GenerateAsync(IAccessCallback callback, Cance cancellationToken.ThrowIfCancellationRequested(); using (await (_asyncLock.LockAsync()).ConfigureAwait(false)) { - if (_hiValue < 0) + var generationState = _stateStore.LocateGenerationState(callback.GetTenantIdentifier()); + + if (generationState.HiValue < 0) { - _value = await (callback.GetNextValueAsync(cancellationToken)).ConfigureAwait(false); - if (_value < 1) + generationState.Value = await (callback.GetNextValueAsync(cancellationToken)).ConfigureAwait(false); + if (generationState.Value < 1) { // unfortunately not really safe to normalize this // to 1 as an initial value like we do the others // because we would not be able to control this if // we are using a sequence... - Log.Info("pooled optimizer source reported [{0}] as the initial value; use of 1 or greater highly recommended", _value); + Log.Info("pooled optimizer source reported [{0}] as the initial value; use of 1 or greater highly recommended", generationState.Value); } - if ((_initialValue == -1 && _value < IncrementSize) || _value == _initialValue) - _hiValue = await (callback.GetNextValueAsync(cancellationToken)).ConfigureAwait(false); + if ((_initialValue == -1 && generationState.Value < IncrementSize) || generationState.Value == _initialValue) + generationState.HiValue = await (callback.GetNextValueAsync(cancellationToken)).ConfigureAwait(false); else { - _hiValue = _value; - _value = _hiValue - IncrementSize; + generationState.HiValue = generationState.Value; + generationState.Value = generationState.HiValue - IncrementSize; } } - else if (_value >= _hiValue) + else if (generationState.Value >= generationState.HiValue) { - _hiValue = await (callback.GetNextValueAsync(cancellationToken)).ConfigureAwait(false); - _value = _hiValue - IncrementSize; + generationState.HiValue = await (callback.GetNextValueAsync(cancellationToken)).ConfigureAwait(false); + generationState.Value = generationState.HiValue - IncrementSize; } - return Make(_value++); + return Make(generationState.Value++); } } } - #endregion #region Nested type: PooledLoOptimizer @@ -149,16 +152,18 @@ public override async Task GenerateAsync(IAccessCallback callback, Cance cancellationToken.ThrowIfCancellationRequested(); using (await (_asyncLock.LockAsync()).ConfigureAwait(false)) { - if (_lastSourceValue < 0 || _value >= (_lastSourceValue + IncrementSize)) + var generationState = _stateStore.LocateGenerationState(callback.GetTenantIdentifier()); + + if (generationState.LastSourceValue < 0 || generationState.Value >= (generationState.LastSourceValue + IncrementSize)) { - _lastSourceValue = await (callback.GetNextValueAsync(cancellationToken)).ConfigureAwait(false); - _value = _lastSourceValue; + generationState.LastSourceValue = await (callback.GetNextValueAsync(cancellationToken)).ConfigureAwait(false); + generationState.Value = generationState.LastSourceValue; // handle cases where initial-value is less than one (hsqldb for instance). - while (_value < 1) - _value++; + while (generationState.Value < 1) + generationState.Value++; } - return Make(_value++); + return Make(generationState.Value++); } } } diff --git a/src/NHibernate/Async/Id/Enhanced/TableGenerator.cs b/src/NHibernate/Async/Id/Enhanced/TableGenerator.cs index 0ca2de00611..0dd7c2dd019 100644 --- a/src/NHibernate/Async/Id/Enhanced/TableGenerator.cs +++ b/src/NHibernate/Async/Id/Enhanced/TableGenerator.cs @@ -9,16 +9,14 @@ using System; -using System.Data.Common; using System.Collections.Generic; using System.Data; -using System.Runtime.CompilerServices; +using System.Data.Common; +using NHibernate.AdoNet.Util; using NHibernate.Engine; -using NHibernate.Mapping; +using NHibernate.SqlCommand; using NHibernate.Type; using NHibernate.Util; -using NHibernate.SqlCommand; -using NHibernate.AdoNet.Util; namespace NHibernate.Id.Enhanced { @@ -44,7 +42,7 @@ private partial class TableAccessCallback : IAccessCallback public async Task GetNextValueAsync(CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); - return Convert.ToInt64(await (owner.DoWorkInNewTransactionAsync(session, cancellationToken)).ConfigureAwait(false)); + return Convert.ToInt64(await (_owner.DoWorkInNewTransactionAsync(_session, cancellationToken)).ConfigureAwait(false)); } #endregion diff --git a/src/NHibernate/Async/Id/SequenceHiLoGenerator.cs b/src/NHibernate/Async/Id/SequenceHiLoGenerator.cs index 75992f456ed..7ed2b4a2ef4 100644 --- a/src/NHibernate/Async/Id/SequenceHiLoGenerator.cs +++ b/src/NHibernate/Async/Id/SequenceHiLoGenerator.cs @@ -10,6 +10,7 @@ using System; using System.Collections; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Runtime.CompilerServices; @@ -39,6 +40,8 @@ public override async Task GenerateAsync(ISessionImplementor session, ob cancellationToken.ThrowIfCancellationRequested(); using (await (_asyncLock.LockAsync()).ConfigureAwait(false)) { + var generationState = _stateStore.LocateGenerationState(session.GetTenantIdentifier()); + if (maxLo < 1) { //keep the behavior consistent even for boundary usages @@ -48,15 +51,15 @@ public override async Task GenerateAsync(ISessionImplementor session, ob return IdentifierGeneratorFactory.CreateNumber(val, returnClass); } - if (lo > maxLo) + if (generationState.Lo > maxLo) { long hival = Convert.ToInt64(await (base.GenerateAsync(session, obj, cancellationToken)).ConfigureAwait(false)); - lo = (hival == 0) ? 1 : 0; - hi = hival * (maxLo + 1); + generationState.Lo = (hival == 0) ? 1 : 0; + generationState.Hi = hival * (maxLo + 1); if (log.IsDebugEnabled()) log.Debug("new hi value: {0}", hival); } - return IdentifierGeneratorFactory.CreateNumber(hi + lo++, returnClass); + return IdentifierGeneratorFactory.CreateNumber(generationState.Hi + generationState.Lo++, returnClass); } } diff --git a/src/NHibernate/Async/Id/TableGenerator.cs b/src/NHibernate/Async/Id/TableGenerator.cs index 3ad468a09be..bc579a777db 100644 --- a/src/NHibernate/Async/Id/TableGenerator.cs +++ b/src/NHibernate/Async/Id/TableGenerator.cs @@ -46,7 +46,7 @@ public virtual async Task GenerateAsync(ISessionImplementor session, obj using (await (_asyncLock.LockAsync()).ConfigureAwait(false)) { // This has to be done using a different connection to the containing - // transaction becase the new hi value must remain valid even if the + // transaction because the new hi value must remain valid even if the // containing transaction rolls back. return await (DoWorkInNewTransactionAsync(session, cancellationToken)).ConfigureAwait(false); } @@ -62,7 +62,7 @@ public override async Task DoWorkInCurrentTransactionAsync(ISessionImple int rows; do { - //the loop ensure atomicitiy of the + //the loop ensures atomicity of the //select + update even for no transaction //or read committed isolation level (needed for .net?) diff --git a/src/NHibernate/Async/Id/TableHiLoGenerator.cs b/src/NHibernate/Async/Id/TableHiLoGenerator.cs index 663733f6b39..760556da853 100644 --- a/src/NHibernate/Async/Id/TableHiLoGenerator.cs +++ b/src/NHibernate/Async/Id/TableHiLoGenerator.cs @@ -16,6 +16,7 @@ using NHibernate.Type; using NHibernate.Util; using System.Collections.Generic; +using System.Collections.Concurrent; namespace NHibernate.Id { @@ -38,6 +39,8 @@ public override async Task GenerateAsync(ISessionImplementor session, ob cancellationToken.ThrowIfCancellationRequested(); using (await (_asyncLock.LockAsync()).ConfigureAwait(false)) { + var generationState = _stateStore.LocateGenerationState(session.GetTenantIdentifier()); + if (maxLo < 1) { //keep the behavior consistent even for boundary usages @@ -46,15 +49,15 @@ public override async Task GenerateAsync(ISessionImplementor session, ob val = Convert.ToInt64(await (base.GenerateAsync(session, obj, cancellationToken)).ConfigureAwait(false)); return IdentifierGeneratorFactory.CreateNumber(val, returnClass); } - if (lo > maxLo) + if (generationState.Lo > maxLo) { long hival = Convert.ToInt64(await (base.GenerateAsync(session, obj, cancellationToken)).ConfigureAwait(false)); - lo = (hival == 0) ? 1 : 0; - hi = hival * (maxLo + 1); + generationState.Lo = (hival == 0) ? 1 : 0; + generationState.Hi = hival * (maxLo + 1); log.Debug("New high value: {0}", hival); } - return IdentifierGeneratorFactory.CreateNumber(hi + lo++, returnClass); + return IdentifierGeneratorFactory.CreateNumber(generationState.Hi + generationState.Lo++, returnClass); } } diff --git a/src/NHibernate/Id/Enhanced/IAccessCallback.cs b/src/NHibernate/Id/Enhanced/IAccessCallback.cs index f072ab68fb8..ec585a2b9c9 100644 --- a/src/NHibernate/Id/Enhanced/IAccessCallback.cs +++ b/src/NHibernate/Id/Enhanced/IAccessCallback.cs @@ -10,5 +10,10 @@ public partial interface IAccessCallback /// Retrieve the next value from the underlying source. /// long GetNextValue(); + + /// + /// Obtain the tenant identifier (multi-tenancy), if one, associated with this callback. + /// + string GetTenantIdentifier(); } -} \ No newline at end of file +} diff --git a/src/NHibernate/Id/Enhanced/OptimizerFactory.cs b/src/NHibernate/Id/Enhanced/OptimizerFactory.cs index 0adf3695551..c54c53fbafe 100644 --- a/src/NHibernate/Id/Enhanced/OptimizerFactory.cs +++ b/src/NHibernate/Id/Enhanced/OptimizerFactory.cs @@ -1,6 +1,6 @@ using System; +using System.Collections.Concurrent; using System.Reflection; -using System.Runtime.CompilerServices; using NHibernate.Util; namespace NHibernate.Id.Enhanced @@ -70,9 +70,9 @@ private static IOptimizer BuildOptimizer(string type, System.Type returnClass, i ConstructorInfo ctor = optimizerClass.GetConstructor(CtorSignature); if (ctor == null) - throw new HibernateException("Optimizer does not have expected contructor"); + throw new HibernateException("Optimizer does not have expected constructor"); - return (IOptimizer)ctor.Invoke(new object[] { returnClass, incrementSize }); + return (IOptimizer) ctor.Invoke(new object[] { returnClass, incrementSize }); } catch (Exception ex) { @@ -89,7 +89,7 @@ public static IOptimizer BuildOptimizer(string type, System.Type returnClass, in IOptimizer optimizer = BuildOptimizer(type, returnClass, incrementSize); if (optimizer is IInitialValueAwareOptimizer) - ((IInitialValueAwareOptimizer)optimizer).InjectInitialValue(explicitInitialValue); + ((IInitialValueAwareOptimizer) optimizer).InjectInitialValue(explicitInitialValue); return optimizer; } @@ -98,10 +98,8 @@ public static IOptimizer BuildOptimizer(string type, System.Type returnClass, in public partial class HiLoOptimizer : OptimizerSupport { - private long _upperLimit; - private long _lastSourceValue = -1; - private long _value; private readonly AsyncLock _asyncLock = new AsyncLock(); + private readonly TenantStateStore _stateStore = new TenantStateStore(); public HiLoOptimizer(System.Type returnClass, int incrementSize) : base(returnClass, incrementSize) { @@ -115,17 +113,24 @@ public HiLoOptimizer(System.Type returnClass, int incrementSize) : base(returnCl } } + public class GenerationState + { + public long LastSourceValue { get; internal set; } = -1; + public long Value { get; internal set; } + public long UpperLimit { get; internal set; } + } + /// /// Exposure intended for testing purposes. /// public override long LastSourceValue { - get { return _lastSourceValue; } + get { return _stateStore.NoTenantGenerationState.LastSourceValue; } } public long LastValue { - get { return _value - 1; } + get { return _stateStore.NoTenantGenerationState.Value - 1; } } /// @@ -133,7 +138,21 @@ public long LastValue /// public long HiValue { - get { return _upperLimit; } + get { return _stateStore.NoTenantGenerationState.UpperLimit; } + } + + public long GetHiValue(string tenantIdentifier) + { + return _stateStore.LocateGenerationState(tenantIdentifier).UpperLimit; + } + + public long GetLastSourceValue(string tenantIdentifier) + { + return _stateStore.LocateGenerationState(tenantIdentifier).LastSourceValue; + } + public long GetLastValue(string tenantIdentifier) + { + return _stateStore.LocateGenerationState(tenantIdentifier).Value - 1; } public override bool ApplyIncrementSizeToSourceValues @@ -145,27 +164,29 @@ public override object Generate(IAccessCallback callback) { using (_asyncLock.Lock()) { - if (_lastSourceValue < 0) + var generationState = _stateStore.LocateGenerationState(callback.GetTenantIdentifier()); + + if (generationState.LastSourceValue < 0) { - _lastSourceValue = callback.GetNextValue(); - while (_lastSourceValue <= 0) + generationState.LastSourceValue = callback.GetNextValue(); + while (generationState.LastSourceValue <= 0) { - _lastSourceValue = callback.GetNextValue(); + generationState.LastSourceValue = callback.GetNextValue(); } // upperLimit defines the upper end of the bucket values - _upperLimit = (_lastSourceValue * IncrementSize) + 1; + generationState.UpperLimit = (generationState.LastSourceValue * IncrementSize) + 1; // initialize value to the low end of the bucket - _value = _upperLimit - IncrementSize; + generationState.Value = generationState.UpperLimit - IncrementSize; } - else if (_upperLimit <= _value) + else if (generationState.UpperLimit <= generationState.Value) { - _lastSourceValue = callback.GetNextValue(); - _upperLimit = (_lastSourceValue * IncrementSize) + 1; + generationState.LastSourceValue = callback.GetNextValue(); + generationState.UpperLimit = (generationState.LastSourceValue * IncrementSize) + 1; } - return Make(_value++); + return Make(generationState.Value++); } } } @@ -268,11 +289,9 @@ protected virtual object Make(long value) /// public partial class PooledOptimizer : OptimizerSupport, IInitialValueAwareOptimizer { - private long _hiValue = -1; - private long _value; private long _initialValue; private readonly AsyncLock _asyncLock = new AsyncLock(); - + private readonly TenantStateStore _stateStore = new TenantStateStore(); public PooledOptimizer(System.Type returnClass, int incrementSize) : base(returnClass, incrementSize) { if (incrementSize < 1) @@ -285,17 +304,28 @@ public PooledOptimizer(System.Type returnClass, int incrementSize) : base(return } } + public class GenerationState + { + public long Value { get; internal set; } + public long HiValue { get; internal set; } = -1; + } + public override long LastSourceValue { - get { return _hiValue; } + get { return _stateStore.NoTenantGenerationState.HiValue; } } /// /// Exposure intended for testing purposes. /// - public long LastValue + /// + public long GetLastSourceValue(string tenantIdentifier) + { + return _stateStore.LocateGenerationState(tenantIdentifier).HiValue; + } + public long GetLastValue(string tenantIdentifier) { - get { return _value - 1; } + return _stateStore.LocateGenerationState(tenantIdentifier).Value - 1; } public override bool ApplyIncrementSizeToSourceValues @@ -312,46 +342,46 @@ public override object Generate(IAccessCallback callback) { using (_asyncLock.Lock()) { - if (_hiValue < 0) + var generationState = _stateStore.LocateGenerationState(callback.GetTenantIdentifier()); + + if (generationState.HiValue < 0) { - _value = callback.GetNextValue(); - if (_value < 1) + generationState.Value = callback.GetNextValue(); + if (generationState.Value < 1) { // unfortunately not really safe to normalize this // to 1 as an initial value like we do the others // because we would not be able to control this if // we are using a sequence... - Log.Info("pooled optimizer source reported [{0}] as the initial value; use of 1 or greater highly recommended", _value); + Log.Info("pooled optimizer source reported [{0}] as the initial value; use of 1 or greater highly recommended", generationState.Value); } - if ((_initialValue == -1 && _value < IncrementSize) || _value == _initialValue) - _hiValue = callback.GetNextValue(); + if ((_initialValue == -1 && generationState.Value < IncrementSize) || generationState.Value == _initialValue) + generationState.HiValue = callback.GetNextValue(); else { - _hiValue = _value; - _value = _hiValue - IncrementSize; + generationState.HiValue = generationState.Value; + generationState.Value = generationState.HiValue - IncrementSize; } } - else if (_value >= _hiValue) + else if (generationState.Value >= generationState.HiValue) { - _hiValue = callback.GetNextValue(); - _value = _hiValue - IncrementSize; + generationState.HiValue = callback.GetNextValue(); + generationState.Value = generationState.HiValue - IncrementSize; } - return Make(_value++); + return Make(generationState.Value++); } } } - #endregion #region Nested type: PooledLoOptimizer public partial class PooledLoOptimizer : OptimizerSupport { - private long _lastSourceValue = -1; // last value read from db source - private long _value; // the current generator value private readonly AsyncLock _asyncLock = new AsyncLock(); + private readonly TenantStateStore _stateStore = new TenantStateStore(); public PooledLoOptimizer(System.Type returnClass, int incrementSize) : base(returnClass, incrementSize) { @@ -365,26 +395,35 @@ public PooledLoOptimizer(System.Type returnClass, int incrementSize) : base(retu } } + public class GenerationState + { + public long LastSourceValue { get; internal set; } = -1; + public long Value { get; internal set; } + } + public override object Generate(IAccessCallback callback) { using (_asyncLock.Lock()) { - if (_lastSourceValue < 0 || _value >= (_lastSourceValue + IncrementSize)) + var generationState = _stateStore.LocateGenerationState(callback.GetTenantIdentifier()); + + if (generationState.LastSourceValue < 0 || generationState.Value >= (generationState.LastSourceValue + IncrementSize)) { - _lastSourceValue = callback.GetNextValue(); - _value = _lastSourceValue; + generationState.LastSourceValue = callback.GetNextValue(); + generationState.Value = generationState.LastSourceValue; // handle cases where initial-value is less than one (hsqldb for instance). - while (_value < 1) - _value++; + while (generationState.Value < 1) + generationState.Value++; } - return Make(_value++); + return Make(generationState.Value++); } } + public override long LastSourceValue { - get { return _lastSourceValue; } + get { return _stateStore.NoTenantGenerationState.LastSourceValue; } } public override bool ApplyIncrementSizeToSourceValues @@ -397,7 +436,17 @@ public override bool ApplyIncrementSizeToSourceValues /// public long LastValue { - get { return _value - 1; } + get { return _stateStore.NoTenantGenerationState.Value - 1; } + } + + public long GetLastSourceValue(string tenantIdentifier) + { + return _stateStore.LocateGenerationState(tenantIdentifier).LastSourceValue; + } + + public long GetLastValue(string tenantIdentifier) + { + return _stateStore.LocateGenerationState(tenantIdentifier).Value - 1; } } diff --git a/src/NHibernate/Id/Enhanced/SequenceStructure.cs b/src/NHibernate/Id/Enhanced/SequenceStructure.cs index 50d70e54964..b035158eba5 100644 --- a/src/NHibernate/Id/Enhanced/SequenceStructure.cs +++ b/src/NHibernate/Id/Enhanced/SequenceStructure.cs @@ -136,6 +136,8 @@ public virtual long GetNextValue() } } + public string GetTenantIdentifier() => _session.GetTenantIdentifier(); + #endregion } diff --git a/src/NHibernate/Id/Enhanced/TableGenerator.cs b/src/NHibernate/Id/Enhanced/TableGenerator.cs index 60a287db13f..fc6148fee5f 100644 --- a/src/NHibernate/Id/Enhanced/TableGenerator.cs +++ b/src/NHibernate/Id/Enhanced/TableGenerator.cs @@ -1,14 +1,12 @@ using System; -using System.Data.Common; using System.Collections.Generic; using System.Data; -using System.Runtime.CompilerServices; +using System.Data.Common; +using NHibernate.AdoNet.Util; using NHibernate.Engine; -using NHibernate.Mapping; +using NHibernate.SqlCommand; using NHibernate.Type; using NHibernate.Util; -using NHibernate.SqlCommand; -using NHibernate.AdoNet.Util; namespace NHibernate.Id.Enhanced { @@ -389,22 +387,24 @@ public virtual object Generate(ISessionImplementor session, object obj) private partial class TableAccessCallback : IAccessCallback { - private TableGenerator owner; - private readonly ISessionImplementor session; + private TableGenerator _owner; + private readonly ISessionImplementor _session; public TableAccessCallback(ISessionImplementor session, TableGenerator owner) { - this.session = session; - this.owner = owner; + _session = session; + _owner = owner; } #region IAccessCallback Members public long GetNextValue() { - return Convert.ToInt64(owner.DoWorkInNewTransaction(session)); + return Convert.ToInt64(_owner.DoWorkInNewTransaction(_session)); } + public string GetTenantIdentifier() => _session.GetTenantIdentifier(); + #endregion } diff --git a/src/NHibernate/Id/Enhanced/TableStructure.cs b/src/NHibernate/Id/Enhanced/TableStructure.cs index 82c36b43a65..bc59e8e081b 100644 --- a/src/NHibernate/Id/Enhanced/TableStructure.cs +++ b/src/NHibernate/Id/Enhanced/TableStructure.cs @@ -181,6 +181,8 @@ public virtual long GetNextValue() return Convert.ToInt64(_owner.DoWorkInNewTransaction(_session)); } + public string GetTenantIdentifier() => _session.GetTenantIdentifier(); + #endregion } diff --git a/src/NHibernate/Id/SequenceHiLoGenerator.cs b/src/NHibernate/Id/SequenceHiLoGenerator.cs index 2d9fca0b85b..475027dde93 100644 --- a/src/NHibernate/Id/SequenceHiLoGenerator.cs +++ b/src/NHibernate/Id/SequenceHiLoGenerator.cs @@ -1,5 +1,6 @@ using System; using System.Collections; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Runtime.CompilerServices; @@ -29,7 +30,7 @@ namespace NHibernate.Id ///

///

/// The user may specify a max_lo value to determine how often new hi values are - /// fetched. If sequences are not avaliable, TableHiLoGenerator might be an + /// fetched. If sequences are not available, TableHiLoGenerator might be an /// alternative. ///

/// @@ -43,11 +44,16 @@ public partial class SequenceHiLoGenerator : SequenceGenerator public const string MaxLo = "max_lo"; private int maxLo; - private int lo; - private long hi; private System.Type returnClass; + private TenantStateStore _stateStore; private readonly AsyncLock _asyncLock = new AsyncLock(); + private class GenerationState + { + public long Lo { get; internal set; } + public long Hi { get; internal set; } + } + #region IConfigurable Members /// @@ -61,8 +67,8 @@ public override void Configure(IType type, IDictionary parms, Di { base.Configure(type, parms, dialect); maxLo = PropertiesHelper.GetInt32(MaxLo, parms, 9); - lo = maxLo + 1; // so we "clock over" on the first invocation returnClass = type.ReturnedClass; + _stateStore = new TenantStateStore(state => state.Lo = maxLo + 1);// so we "clock over" on the first invocation } #endregion @@ -80,6 +86,8 @@ public override object Generate(ISessionImplementor session, object obj) { using (_asyncLock.Lock()) { + var generationState = _stateStore.LocateGenerationState(session.GetTenantIdentifier()); + if (maxLo < 1) { //keep the behavior consistent even for boundary usages @@ -89,15 +97,15 @@ public override object Generate(ISessionImplementor session, object obj) return IdentifierGeneratorFactory.CreateNumber(val, returnClass); } - if (lo > maxLo) + if (generationState.Lo > maxLo) { long hival = Convert.ToInt64(base.Generate(session, obj)); - lo = (hival == 0) ? 1 : 0; - hi = hival * (maxLo + 1); + generationState.Lo = (hival == 0) ? 1 : 0; + generationState.Hi = hival * (maxLo + 1); if (log.IsDebugEnabled()) log.Debug("new hi value: {0}", hival); } - return IdentifierGeneratorFactory.CreateNumber(hi + lo++, returnClass); + return IdentifierGeneratorFactory.CreateNumber(generationState.Hi + generationState.Lo++, returnClass); } } diff --git a/src/NHibernate/Id/TableGenerator.cs b/src/NHibernate/Id/TableGenerator.cs index 09180687efc..3fbd16e4015 100644 --- a/src/NHibernate/Id/TableGenerator.cs +++ b/src/NHibernate/Id/TableGenerator.cs @@ -157,7 +157,7 @@ public virtual object Generate(ISessionImplementor session, object obj) using (_asyncLock.Lock()) { // This has to be done using a different connection to the containing - // transaction becase the new hi value must remain valid even if the + // transaction because the new hi value must remain valid even if the // containing transaction rolls back. return DoWorkInNewTransaction(session); } @@ -220,7 +220,7 @@ public override object DoWorkInCurrentTransaction(ISessionImplementor session, D int rows; do { - //the loop ensure atomicitiy of the + //the loop ensures atomicity of the //select + update even for no transaction //or read committed isolation level (needed for .net?) diff --git a/src/NHibernate/Id/TableHiLoGenerator.cs b/src/NHibernate/Id/TableHiLoGenerator.cs index c64de1d3323..2ab763bf78e 100644 --- a/src/NHibernate/Id/TableHiLoGenerator.cs +++ b/src/NHibernate/Id/TableHiLoGenerator.cs @@ -6,6 +6,7 @@ using NHibernate.Type; using NHibernate.Util; using System.Collections.Generic; +using System.Collections.Concurrent; namespace NHibernate.Id { @@ -48,12 +49,17 @@ public partial class TableHiLoGenerator : TableGenerator /// public const string MaxLo = "max_lo"; - private long hi; - private long lo; private long maxLo; private System.Type returnClass; + private TenantStateStore _stateStore; private readonly AsyncLock _asyncLock = new AsyncLock(); + private class GenerationState + { + public long Lo { get; internal set; } + public long Hi { get; internal set; } + } + #region IConfigurable Members /// @@ -67,8 +73,8 @@ public override void Configure(IType type, IDictionary parms, Di { base.Configure(type, parms, dialect); maxLo = PropertiesHelper.GetInt64(MaxLo, parms, Int16.MaxValue); - lo = maxLo + 1; // so we "clock over" on the first invocation returnClass = type.ReturnedClass; + _stateStore = new TenantStateStore(state => state.Lo = maxLo + 1);// so we "clock over" on the first invocation } #endregion @@ -85,6 +91,8 @@ public override object Generate(ISessionImplementor session, object obj) { using (_asyncLock.Lock()) { + var generationState = _stateStore.LocateGenerationState(session.GetTenantIdentifier()); + if (maxLo < 1) { //keep the behavior consistent even for boundary usages @@ -93,18 +101,61 @@ public override object Generate(ISessionImplementor session, object obj) val = Convert.ToInt64(base.Generate(session, obj)); return IdentifierGeneratorFactory.CreateNumber(val, returnClass); } - if (lo > maxLo) + if (generationState.Lo > maxLo) { long hival = Convert.ToInt64(base.Generate(session, obj)); - lo = (hival == 0) ? 1 : 0; - hi = hival * (maxLo + 1); + generationState.Lo = (hival == 0) ? 1 : 0; + generationState.Hi = hival * (maxLo + 1); log.Debug("New high value: {0}", hival); } - return IdentifierGeneratorFactory.CreateNumber(hi + lo++, returnClass); + return IdentifierGeneratorFactory.CreateNumber(generationState.Hi + generationState.Lo++, returnClass); } } #endregion } + + internal class TenantStateStore where TState : new() + { + private TState _noTenantState; + private Action _initializer; + private readonly Lazy> _tenantSpecificState = new Lazy>(() => new ConcurrentDictionary()); + + public TenantStateStore(Action initializer) + { + _initializer = initializer; + } + + public TenantStateStore() + { + } + + internal TState LocateGenerationState(string tenantIdentifier) + { + if (tenantIdentifier == null) + { + if (_noTenantState == null) + { + _noTenantState = CreateNewState(); + } + return _noTenantState; + } + else + { + return _tenantSpecificState.Value.GetOrAdd(tenantIdentifier, _ => CreateNewState()); + } + } + + internal TState NoTenantGenerationState => _noTenantState ?? + throw new HibernateException("Could not locate previous generation state for no-tenant"); + + + private TState CreateNewState() + { + var state = new TState(); + _initializer?.Invoke(state); + return state; + } + } } From 553a95153a8697ca5428a8e9bba0313bc76a8566 Mon Sep 17 00:00:00 2001 From: gliljas Date: Tue, 11 Apr 2023 17:22:49 +0200 Subject: [PATCH 2/5] Split file --- src/NHibernate/Id/TableHiLoGenerator.cs | 43 ---------------------- src/NHibernate/Id/TenantStateStore.cs | 48 +++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 43 deletions(-) create mode 100644 src/NHibernate/Id/TenantStateStore.cs diff --git a/src/NHibernate/Id/TableHiLoGenerator.cs b/src/NHibernate/Id/TableHiLoGenerator.cs index 2ab763bf78e..1629fc7430e 100644 --- a/src/NHibernate/Id/TableHiLoGenerator.cs +++ b/src/NHibernate/Id/TableHiLoGenerator.cs @@ -115,47 +115,4 @@ public override object Generate(ISessionImplementor session, object obj) #endregion } - - internal class TenantStateStore where TState : new() - { - private TState _noTenantState; - private Action _initializer; - private readonly Lazy> _tenantSpecificState = new Lazy>(() => new ConcurrentDictionary()); - - public TenantStateStore(Action initializer) - { - _initializer = initializer; - } - - public TenantStateStore() - { - } - - internal TState LocateGenerationState(string tenantIdentifier) - { - if (tenantIdentifier == null) - { - if (_noTenantState == null) - { - _noTenantState = CreateNewState(); - } - return _noTenantState; - } - else - { - return _tenantSpecificState.Value.GetOrAdd(tenantIdentifier, _ => CreateNewState()); - } - } - - internal TState NoTenantGenerationState => _noTenantState ?? - throw new HibernateException("Could not locate previous generation state for no-tenant"); - - - private TState CreateNewState() - { - var state = new TState(); - _initializer?.Invoke(state); - return state; - } - } } diff --git a/src/NHibernate/Id/TenantStateStore.cs b/src/NHibernate/Id/TenantStateStore.cs new file mode 100644 index 00000000000..fbb4d8372a6 --- /dev/null +++ b/src/NHibernate/Id/TenantStateStore.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Concurrent; + +namespace NHibernate.Id +{ + internal class TenantStateStore where TState : new() + { + private TState _noTenantState; + private Action _initializer; + private readonly Lazy> _tenantSpecificState = new Lazy>(() => new ConcurrentDictionary()); + + public TenantStateStore(Action initializer) + { + _initializer = initializer; + } + + public TenantStateStore() + { + } + + internal TState LocateGenerationState(string tenantIdentifier) + { + if (tenantIdentifier == null) + { + if (_noTenantState == null) + { + _noTenantState = CreateNewState(); + } + return _noTenantState; + } + else + { + return _tenantSpecificState.Value.GetOrAdd(tenantIdentifier, _ => CreateNewState()); + } + } + + internal TState NoTenantGenerationState => _noTenantState ?? + throw new HibernateException("Could not locate previous generation state for no-tenant"); + + + private TState CreateNewState() + { + var state = new TState(); + _initializer?.Invoke(state); + return state; + } + } +} From dfa4734eaa6a2550784262d04eb41ed1d339162b Mon Sep 17 00:00:00 2001 From: gliljas Date: Tue, 11 Apr 2023 18:36:48 +0200 Subject: [PATCH 3/5] Fix for #3229 --- src/NHibernate/Id/Enhanced/OptimizerFactory.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/NHibernate/Id/Enhanced/OptimizerFactory.cs b/src/NHibernate/Id/Enhanced/OptimizerFactory.cs index c54c53fbafe..d599ce49521 100644 --- a/src/NHibernate/Id/Enhanced/OptimizerFactory.cs +++ b/src/NHibernate/Id/Enhanced/OptimizerFactory.cs @@ -412,8 +412,10 @@ public override object Generate(IAccessCallback callback) generationState.LastSourceValue = callback.GetNextValue(); generationState.Value = generationState.LastSourceValue; // handle cases where initial-value is less than one (hsqldb for instance). - while (generationState.Value < 1) - generationState.Value++; + if (generationState.Value < 1) + { + generationState.Value = 1; + } } return Make(generationState.Value++); From e63dca4599cecbaebb5ac94d3a46c842f7eb7788 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 11 Apr 2023 16:40:25 +0000 Subject: [PATCH 4/5] Generate async files --- src/NHibernate/Async/Id/Enhanced/OptimizerFactory.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/NHibernate/Async/Id/Enhanced/OptimizerFactory.cs b/src/NHibernate/Async/Id/Enhanced/OptimizerFactory.cs index 84f66d725ce..f18744e6c08 100644 --- a/src/NHibernate/Async/Id/Enhanced/OptimizerFactory.cs +++ b/src/NHibernate/Async/Id/Enhanced/OptimizerFactory.cs @@ -159,8 +159,10 @@ public override async Task GenerateAsync(IAccessCallback callback, Cance generationState.LastSourceValue = await (callback.GetNextValueAsync(cancellationToken)).ConfigureAwait(false); generationState.Value = generationState.LastSourceValue; // handle cases where initial-value is less than one (hsqldb for instance). - while (generationState.Value < 1) - generationState.Value++; + if (generationState.Value < 1) + { + generationState.Value = 1; + } } return Make(generationState.Value++); From ff20724fe3b5d23187e5ff86b7565d8bd8817fb4 Mon Sep 17 00:00:00 2001 From: gliljas Date: Wed, 12 Apr 2023 17:30:52 +0200 Subject: [PATCH 5/5] Corrections in review --- .../HiLoForcedTableSequenceTest.cs | 12 ++++++++ .../PooledForcedTableSequenceTest.cs | 13 +++++++- .../Enhanced/Sequence/HiLoSequenceTest.cs | 12 ++++++++ .../Enhanced/Sequence/PooledSequenceTest.cs | 10 +++++++ .../IdGen/Enhanced/Table/HiLoTableTest.cs | 12 ++++++++ .../IdGen/Enhanced/Table/PooledLoTableTest.cs | 10 +++++++ .../IdGen/Enhanced/Table/PooledTableTest.cs | 10 +++++++ .../HiLoForcedTableSequenceTest.cs | 12 ++++++++ .../PooledForcedTableSequenceTest.cs | 13 +++++++- .../Enhanced/Sequence/HiLoSequenceTest.cs | 12 ++++++++ .../Enhanced/Sequence/PooledSequenceTest.cs | 10 +++++++ .../IdGen/Enhanced/Table/HiLoTableTest.cs | 12 ++++++++ .../IdGen/Enhanced/Table/PooledLoTableTest.cs | 10 +++++++ .../IdGen/Enhanced/Table/PooledTableTest.cs | 10 +++++++ .../Async/Id/Enhanced/OptimizerFactory.cs | 22 +++++++------- .../Async/Id/SequenceHiLoGenerator.cs | 7 +++-- src/NHibernate/Async/Id/TableHiLoGenerator.cs | 7 +++-- .../Id/Enhanced/OptimizerFactory.cs | 30 +++++++++++-------- src/NHibernate/Id/SequenceHiLoGenerator.cs | 10 +++---- src/NHibernate/Id/TableHiLoGenerator.cs | 10 +++---- src/NHibernate/Id/TenantStateStore.cs | 18 +++++------ 21 files changed, 211 insertions(+), 51 deletions(-) diff --git a/src/NHibernate.Test/Async/IdGen/Enhanced/Forcedtable/HiLoForcedTableSequenceTest.cs b/src/NHibernate.Test/Async/IdGen/Enhanced/Forcedtable/HiLoForcedTableSequenceTest.cs index abf9104e83f..0cff9650e6a 100644 --- a/src/NHibernate.Test/Async/IdGen/Enhanced/Forcedtable/HiLoForcedTableSequenceTest.cs +++ b/src/NHibernate.Test/Async/IdGen/Enhanced/Forcedtable/HiLoForcedTableSequenceTest.cs @@ -58,6 +58,12 @@ public async Task TestNormalBoundaryAsync() long expectedId = i + 1; Assert.That(entities[i].Id, Is.EqualTo(expectedId)); Assert.That(generator.DatabaseStructure.TimesAccessed, Is.EqualTo(1)); // initialization + if (TenantIdentifier == null) + { + Assert.That(optimizer.LastSourceValue, Is.EqualTo(1)); // initialization + Assert.That(optimizer.LastValue, Is.EqualTo(i + 1)); + Assert.That(optimizer.HiValue, Is.EqualTo(increment + 1)); + } Assert.That(optimizer.GetLastSourceValue(TenantIdentifier), Is.EqualTo(1)); // initialization Assert.That(optimizer.GetLastValue(TenantIdentifier), Is.EqualTo(i + 1)); Assert.That(optimizer.GetHiValue(TenantIdentifier), Is.EqualTo(increment + 1)); @@ -68,6 +74,12 @@ public async Task TestNormalBoundaryAsync() await (session.SaveAsync(entities[increment])); Assert.That(entities[optimizer.IncrementSize].Id, Is.EqualTo(optimizer.IncrementSize + 1)); Assert.That(generator.DatabaseStructure.TimesAccessed, Is.EqualTo(2)); // initialization + clock-over + if (TenantIdentifier == null) + { + Assert.That(optimizer.LastSourceValue, Is.EqualTo(2)); // initialization + clock-over + Assert.That(optimizer.LastValue, Is.EqualTo(increment + 1)); + Assert.That(optimizer.HiValue, Is.EqualTo(increment * 2 + 1)); + } Assert.That(optimizer.GetLastSourceValue(TenantIdentifier), Is.EqualTo(2)); // initialization + clock-over Assert.That(optimizer.GetLastValue(TenantIdentifier), Is.EqualTo(increment + 1)); Assert.That(optimizer.GetHiValue(TenantIdentifier), Is.EqualTo(increment * 2 + 1)); diff --git a/src/NHibernate.Test/Async/IdGen/Enhanced/Forcedtable/PooledForcedTableSequenceTest.cs b/src/NHibernate.Test/Async/IdGen/Enhanced/Forcedtable/PooledForcedTableSequenceTest.cs index 9f5f3a4168a..03924789aaa 100644 --- a/src/NHibernate.Test/Async/IdGen/Enhanced/Forcedtable/PooledForcedTableSequenceTest.cs +++ b/src/NHibernate.Test/Async/IdGen/Enhanced/Forcedtable/PooledForcedTableSequenceTest.cs @@ -59,6 +59,11 @@ public async Task TestNormalBoundaryAsync() Assert.That(entities[i].Id, Is.EqualTo(expectedId)); // NOTE : initialization calls table twice Assert.That(generator.DatabaseStructure.TimesAccessed, Is.EqualTo(2)); // initialization + if (TenantIdentifier == null) + { + Assert.That(optimizer.LastSourceValue, Is.EqualTo(increment + 1)); // initialization + Assert.That(optimizer.LastValue, Is.EqualTo(i + 1)); + } Assert.That(optimizer.GetLastSourceValue(TenantIdentifier), Is.EqualTo(increment + 1)); // initialization Assert.That(optimizer.GetLastValue(TenantIdentifier), Is.EqualTo(i + 1)); } @@ -70,9 +75,15 @@ public async Task TestNormalBoundaryAsync() Assert.That(entities[optimizer.IncrementSize].Id, Is.EqualTo(optimizer.IncrementSize + 1)); // initialization (2) + clock over Assert.That(generator.DatabaseStructure.TimesAccessed, Is.EqualTo(3)); - Assert.That(optimizer.GetLastSourceValue(TenantIdentifier), Is.EqualTo(increment*2 + 1)); + if (TenantIdentifier == null) + { + Assert.That(optimizer.LastSourceValue, Is.EqualTo(increment * 2 + 1)); + Assert.That(optimizer.LastValue, Is.EqualTo(increment + 1)); + } + Assert.That(optimizer.GetLastSourceValue(TenantIdentifier), Is.EqualTo(increment * 2 + 1)); Assert.That(optimizer.GetLastValue(TenantIdentifier), Is.EqualTo(increment + 1)); + await (transaction.CommitAsync()); } diff --git a/src/NHibernate.Test/Async/IdGen/Enhanced/Sequence/HiLoSequenceTest.cs b/src/NHibernate.Test/Async/IdGen/Enhanced/Sequence/HiLoSequenceTest.cs index 4b1606cf330..9942b263e6a 100644 --- a/src/NHibernate.Test/Async/IdGen/Enhanced/Sequence/HiLoSequenceTest.cs +++ b/src/NHibernate.Test/Async/IdGen/Enhanced/Sequence/HiLoSequenceTest.cs @@ -56,6 +56,12 @@ public async Task TestNormalBoundaryAsync() entities[i] = new Entity("" + (i + 1)); await (session.SaveAsync(entities[i])); Assert.That(generator.DatabaseStructure.TimesAccessed, Is.EqualTo(1)); // initialization + if (TenantIdentifier == null) + { + Assert.That(optimizer.LastSourceValue, Is.EqualTo(1)); // initialization + Assert.That(optimizer.LastValue, Is.EqualTo(i + 1)); + Assert.That(optimizer.HiValue, Is.EqualTo(increment + 1)); + } Assert.That(optimizer.GetLastSourceValue(TenantIdentifier), Is.EqualTo(1)); // initialization Assert.That(optimizer.GetLastValue(TenantIdentifier), Is.EqualTo(i + 1)); Assert.That(optimizer.GetHiValue(TenantIdentifier), Is.EqualTo(increment + 1)); @@ -65,6 +71,12 @@ public async Task TestNormalBoundaryAsync() entities[increment] = new Entity("" + increment); await (session.SaveAsync(entities[increment])); Assert.That(generator.DatabaseStructure.TimesAccessed, Is.EqualTo(2)); + if (TenantIdentifier == null) + { + Assert.That(optimizer.LastSourceValue, Is.EqualTo(2)); + Assert.That(optimizer.LastValue, Is.EqualTo(increment + 1)); + Assert.That(optimizer.HiValue, Is.EqualTo(increment * 2 + 1)); + } Assert.That(optimizer.GetLastSourceValue(TenantIdentifier), Is.EqualTo(2)); Assert.That(optimizer.GetLastValue(TenantIdentifier), Is.EqualTo(increment + 1)); Assert.That(optimizer.GetHiValue(TenantIdentifier), Is.EqualTo(increment * 2 + 1)); diff --git a/src/NHibernate.Test/Async/IdGen/Enhanced/Sequence/PooledSequenceTest.cs b/src/NHibernate.Test/Async/IdGen/Enhanced/Sequence/PooledSequenceTest.cs index 1d06ae11d89..47c2ca1a82d 100644 --- a/src/NHibernate.Test/Async/IdGen/Enhanced/Sequence/PooledSequenceTest.cs +++ b/src/NHibernate.Test/Async/IdGen/Enhanced/Sequence/PooledSequenceTest.cs @@ -55,6 +55,11 @@ public async Task TestNormalBoundaryAsync() entities[i] = new Entity("" + (i + 1)); await (session.SaveAsync(entities[i])); Assert.That(generator.DatabaseStructure.TimesAccessed, Is.EqualTo(2)); // initialization calls seq twice + if (TenantIdentifier == null) + { + Assert.That(optimizer.LastSourceValue, Is.EqualTo(increment + 1)); // initialization calls seq twice + Assert.That(optimizer.LastValue, Is.EqualTo(i + 1)); + } Assert.That(optimizer.GetLastSourceValue(TenantIdentifier), Is.EqualTo(increment + 1)); // initialization calls seq twice Assert.That(optimizer.GetLastValue(TenantIdentifier), Is.EqualTo(i + 1)); } @@ -63,6 +68,11 @@ public async Task TestNormalBoundaryAsync() entities[increment] = new Entity("" + increment); await (session.SaveAsync(entities[increment])); Assert.That(generator.DatabaseStructure.TimesAccessed, Is.EqualTo(3)); // initialization (2) + clock over + if (TenantIdentifier == null) + { + Assert.That(optimizer.LastSourceValue, Is.EqualTo(increment * 2 + 1)); // initialization (2) + clock over + Assert.That(optimizer.LastValue, Is.EqualTo(increment + 1)); + } Assert.That(optimizer.GetLastSourceValue(TenantIdentifier), Is.EqualTo(increment * 2 + 1)); // initialization (2) + clock over Assert.That(optimizer.GetLastValue(TenantIdentifier), Is.EqualTo(increment + 1)); diff --git a/src/NHibernate.Test/Async/IdGen/Enhanced/Table/HiLoTableTest.cs b/src/NHibernate.Test/Async/IdGen/Enhanced/Table/HiLoTableTest.cs index 2e53c916aea..9938d691506 100644 --- a/src/NHibernate.Test/Async/IdGen/Enhanced/Table/HiLoTableTest.cs +++ b/src/NHibernate.Test/Async/IdGen/Enhanced/Table/HiLoTableTest.cs @@ -55,6 +55,12 @@ public async Task TestNormalBoundaryAsync() entities[i] = new Entity("" + (i + 1)); await (s.SaveAsync(entities[i])); Assert.That(generator.TableAccessCount, Is.EqualTo(1)); // initialization + if (TenantIdentifier == null) + { + Assert.That(optimizer.LastSourceValue, Is.EqualTo(1)); // initialization + Assert.That(optimizer.LastValue, Is.EqualTo(i + 1)); + Assert.That(optimizer.HiValue, Is.EqualTo(increment + 1)); + } Assert.That(optimizer.GetLastSourceValue(TenantIdentifier), Is.EqualTo(1)); // initialization Assert.That(optimizer.GetLastValue(TenantIdentifier), Is.EqualTo(i + 1)); Assert.That(optimizer.GetHiValue(TenantIdentifier), Is.EqualTo(increment + 1)); @@ -64,6 +70,12 @@ public async Task TestNormalBoundaryAsync() entities[increment] = new Entity("" + increment); await (s.SaveAsync(entities[increment])); Assert.That(generator.TableAccessCount, Is.EqualTo(2)); + if (TenantIdentifier == null) + { + Assert.That(optimizer.LastSourceValue, Is.EqualTo(2)); + Assert.That(optimizer.LastValue, Is.EqualTo(increment + 1)); + Assert.That(optimizer.HiValue, Is.EqualTo((increment * 2) + 1)); + } Assert.That(optimizer.GetLastSourceValue(TenantIdentifier), Is.EqualTo(2)); Assert.That(optimizer.GetLastValue(TenantIdentifier), Is.EqualTo(increment + 1)); Assert.That(optimizer.GetHiValue(TenantIdentifier), Is.EqualTo((increment * 2) + 1)); diff --git a/src/NHibernate.Test/Async/IdGen/Enhanced/Table/PooledLoTableTest.cs b/src/NHibernate.Test/Async/IdGen/Enhanced/Table/PooledLoTableTest.cs index af309699513..a5afc87bb50 100644 --- a/src/NHibernate.Test/Async/IdGen/Enhanced/Table/PooledLoTableTest.cs +++ b/src/NHibernate.Test/Async/IdGen/Enhanced/Table/PooledLoTableTest.cs @@ -55,6 +55,11 @@ public async Task TestNormalBoundaryAsync() entities[i] = new Entity("" + (i + 1)); await (s.SaveAsync(entities[i])); Assert.That(generator.TableAccessCount, Is.EqualTo(1)); // initialization + if (TenantIdentifier == null) + { + Assert.That(optimizer.LastSourceValue, Is.EqualTo(1)); // initialization + Assert.That(optimizer.LastValue, Is.EqualTo(i + 1)); + } Assert.That(optimizer.GetLastSourceValue(TenantIdentifier), Is.EqualTo(1)); // initialization Assert.That(optimizer.GetLastValue(TenantIdentifier), Is.EqualTo(i + 1)); } @@ -63,6 +68,11 @@ public async Task TestNormalBoundaryAsync() entities[increment] = new Entity("" + increment); await (s.SaveAsync(entities[increment])); Assert.That(generator.TableAccessCount, Is.EqualTo(2)); + if (TenantIdentifier == null) + { + Assert.That(optimizer.LastSourceValue, Is.EqualTo(increment + 1)); + Assert.That(optimizer.LastValue, Is.EqualTo(increment + 1)); + } Assert.That(optimizer.GetLastSourceValue(TenantIdentifier), Is.EqualTo(increment + 1)); Assert.That(optimizer.GetLastValue(TenantIdentifier), Is.EqualTo(increment + 1)); diff --git a/src/NHibernate.Test/Async/IdGen/Enhanced/Table/PooledTableTest.cs b/src/NHibernate.Test/Async/IdGen/Enhanced/Table/PooledTableTest.cs index 15be48dccaa..439de62e4cd 100644 --- a/src/NHibernate.Test/Async/IdGen/Enhanced/Table/PooledTableTest.cs +++ b/src/NHibernate.Test/Async/IdGen/Enhanced/Table/PooledTableTest.cs @@ -54,6 +54,11 @@ public async Task TestNormalBoundaryAsync() entities[i] = new Entity("" + (i + 1)); await (s.SaveAsync(entities[i])); Assert.That(generator.TableAccessCount, Is.EqualTo(2)); // initialization calls seq twice + if (TenantIdentifier == null) + { + Assert.That(optimizer.LastSourceValue, Is.EqualTo(increment + 1)); // initialization calls seq twice + Assert.That(optimizer.LastValue, Is.EqualTo(i + 1)); + } Assert.That(optimizer.GetLastSourceValue(TenantIdentifier), Is.EqualTo(increment + 1)); // initialization calls seq twice Assert.That(optimizer.GetLastValue(TenantIdentifier), Is.EqualTo(i + 1)); } @@ -62,6 +67,11 @@ public async Task TestNormalBoundaryAsync() entities[increment] = new Entity("" + increment); await (s.SaveAsync(entities[increment])); Assert.That(generator.TableAccessCount, Is.EqualTo(3)); // initialization (2) + clock over + if (TenantIdentifier == null) + { + Assert.That(optimizer.LastSourceValue, Is.EqualTo((increment * 2) + 1)); // initialization (2) + clock over + Assert.That(optimizer.LastValue, Is.EqualTo(increment + 1)); + } Assert.That(optimizer.GetLastSourceValue(TenantIdentifier), Is.EqualTo((increment * 2) + 1)); // initialization (2) + clock over Assert.That(optimizer.GetLastValue(TenantIdentifier), Is.EqualTo(increment + 1)); diff --git a/src/NHibernate.Test/IdGen/Enhanced/Forcedtable/HiLoForcedTableSequenceTest.cs b/src/NHibernate.Test/IdGen/Enhanced/Forcedtable/HiLoForcedTableSequenceTest.cs index e8647f3858f..2411dfada16 100644 --- a/src/NHibernate.Test/IdGen/Enhanced/Forcedtable/HiLoForcedTableSequenceTest.cs +++ b/src/NHibernate.Test/IdGen/Enhanced/Forcedtable/HiLoForcedTableSequenceTest.cs @@ -47,6 +47,12 @@ public void TestNormalBoundary() long expectedId = i + 1; Assert.That(entities[i].Id, Is.EqualTo(expectedId)); Assert.That(generator.DatabaseStructure.TimesAccessed, Is.EqualTo(1)); // initialization + if (TenantIdentifier == null) + { + Assert.That(optimizer.LastSourceValue, Is.EqualTo(1)); // initialization + Assert.That(optimizer.LastValue, Is.EqualTo(i + 1)); + Assert.That(optimizer.HiValue, Is.EqualTo(increment + 1)); + } Assert.That(optimizer.GetLastSourceValue(TenantIdentifier), Is.EqualTo(1)); // initialization Assert.That(optimizer.GetLastValue(TenantIdentifier), Is.EqualTo(i + 1)); Assert.That(optimizer.GetHiValue(TenantIdentifier), Is.EqualTo(increment + 1)); @@ -57,6 +63,12 @@ public void TestNormalBoundary() session.Save(entities[increment]); Assert.That(entities[optimizer.IncrementSize].Id, Is.EqualTo(optimizer.IncrementSize + 1)); Assert.That(generator.DatabaseStructure.TimesAccessed, Is.EqualTo(2)); // initialization + clock-over + if (TenantIdentifier == null) + { + Assert.That(optimizer.LastSourceValue, Is.EqualTo(2)); // initialization + clock-over + Assert.That(optimizer.LastValue, Is.EqualTo(increment + 1)); + Assert.That(optimizer.HiValue, Is.EqualTo(increment * 2 + 1)); + } Assert.That(optimizer.GetLastSourceValue(TenantIdentifier), Is.EqualTo(2)); // initialization + clock-over Assert.That(optimizer.GetLastValue(TenantIdentifier), Is.EqualTo(increment + 1)); Assert.That(optimizer.GetHiValue(TenantIdentifier), Is.EqualTo(increment * 2 + 1)); diff --git a/src/NHibernate.Test/IdGen/Enhanced/Forcedtable/PooledForcedTableSequenceTest.cs b/src/NHibernate.Test/IdGen/Enhanced/Forcedtable/PooledForcedTableSequenceTest.cs index 39598c9d591..4add7229bba 100644 --- a/src/NHibernate.Test/IdGen/Enhanced/Forcedtable/PooledForcedTableSequenceTest.cs +++ b/src/NHibernate.Test/IdGen/Enhanced/Forcedtable/PooledForcedTableSequenceTest.cs @@ -48,6 +48,11 @@ public void TestNormalBoundary() Assert.That(entities[i].Id, Is.EqualTo(expectedId)); // NOTE : initialization calls table twice Assert.That(generator.DatabaseStructure.TimesAccessed, Is.EqualTo(2)); // initialization + if (TenantIdentifier == null) + { + Assert.That(optimizer.LastSourceValue, Is.EqualTo(increment + 1)); // initialization + Assert.That(optimizer.LastValue, Is.EqualTo(i + 1)); + } Assert.That(optimizer.GetLastSourceValue(TenantIdentifier), Is.EqualTo(increment + 1)); // initialization Assert.That(optimizer.GetLastValue(TenantIdentifier), Is.EqualTo(i + 1)); } @@ -59,9 +64,15 @@ public void TestNormalBoundary() Assert.That(entities[optimizer.IncrementSize].Id, Is.EqualTo(optimizer.IncrementSize + 1)); // initialization (2) + clock over Assert.That(generator.DatabaseStructure.TimesAccessed, Is.EqualTo(3)); - Assert.That(optimizer.GetLastSourceValue(TenantIdentifier), Is.EqualTo(increment*2 + 1)); + if (TenantIdentifier == null) + { + Assert.That(optimizer.LastSourceValue, Is.EqualTo(increment * 2 + 1)); + Assert.That(optimizer.LastValue, Is.EqualTo(increment + 1)); + } + Assert.That(optimizer.GetLastSourceValue(TenantIdentifier), Is.EqualTo(increment * 2 + 1)); Assert.That(optimizer.GetLastValue(TenantIdentifier), Is.EqualTo(increment + 1)); + transaction.Commit(); } diff --git a/src/NHibernate.Test/IdGen/Enhanced/Sequence/HiLoSequenceTest.cs b/src/NHibernate.Test/IdGen/Enhanced/Sequence/HiLoSequenceTest.cs index 7cd6161bc02..10db766ed8b 100644 --- a/src/NHibernate.Test/IdGen/Enhanced/Sequence/HiLoSequenceTest.cs +++ b/src/NHibernate.Test/IdGen/Enhanced/Sequence/HiLoSequenceTest.cs @@ -45,6 +45,12 @@ public void TestNormalBoundary() entities[i] = new Entity("" + (i + 1)); session.Save(entities[i]); Assert.That(generator.DatabaseStructure.TimesAccessed, Is.EqualTo(1)); // initialization + if (TenantIdentifier == null) + { + Assert.That(optimizer.LastSourceValue, Is.EqualTo(1)); // initialization + Assert.That(optimizer.LastValue, Is.EqualTo(i + 1)); + Assert.That(optimizer.HiValue, Is.EqualTo(increment + 1)); + } Assert.That(optimizer.GetLastSourceValue(TenantIdentifier), Is.EqualTo(1)); // initialization Assert.That(optimizer.GetLastValue(TenantIdentifier), Is.EqualTo(i + 1)); Assert.That(optimizer.GetHiValue(TenantIdentifier), Is.EqualTo(increment + 1)); @@ -54,6 +60,12 @@ public void TestNormalBoundary() entities[increment] = new Entity("" + increment); session.Save(entities[increment]); Assert.That(generator.DatabaseStructure.TimesAccessed, Is.EqualTo(2)); + if (TenantIdentifier == null) + { + Assert.That(optimizer.LastSourceValue, Is.EqualTo(2)); + Assert.That(optimizer.LastValue, Is.EqualTo(increment + 1)); + Assert.That(optimizer.HiValue, Is.EqualTo(increment * 2 + 1)); + } Assert.That(optimizer.GetLastSourceValue(TenantIdentifier), Is.EqualTo(2)); Assert.That(optimizer.GetLastValue(TenantIdentifier), Is.EqualTo(increment + 1)); Assert.That(optimizer.GetHiValue(TenantIdentifier), Is.EqualTo(increment * 2 + 1)); diff --git a/src/NHibernate.Test/IdGen/Enhanced/Sequence/PooledSequenceTest.cs b/src/NHibernate.Test/IdGen/Enhanced/Sequence/PooledSequenceTest.cs index b18c211b7ba..669c03376db 100644 --- a/src/NHibernate.Test/IdGen/Enhanced/Sequence/PooledSequenceTest.cs +++ b/src/NHibernate.Test/IdGen/Enhanced/Sequence/PooledSequenceTest.cs @@ -44,6 +44,11 @@ public void TestNormalBoundary() entities[i] = new Entity("" + (i + 1)); session.Save(entities[i]); Assert.That(generator.DatabaseStructure.TimesAccessed, Is.EqualTo(2)); // initialization calls seq twice + if (TenantIdentifier == null) + { + Assert.That(optimizer.LastSourceValue, Is.EqualTo(increment + 1)); // initialization calls seq twice + Assert.That(optimizer.LastValue, Is.EqualTo(i + 1)); + } Assert.That(optimizer.GetLastSourceValue(TenantIdentifier), Is.EqualTo(increment + 1)); // initialization calls seq twice Assert.That(optimizer.GetLastValue(TenantIdentifier), Is.EqualTo(i + 1)); } @@ -52,6 +57,11 @@ public void TestNormalBoundary() entities[increment] = new Entity("" + increment); session.Save(entities[increment]); Assert.That(generator.DatabaseStructure.TimesAccessed, Is.EqualTo(3)); // initialization (2) + clock over + if (TenantIdentifier == null) + { + Assert.That(optimizer.LastSourceValue, Is.EqualTo(increment * 2 + 1)); // initialization (2) + clock over + Assert.That(optimizer.LastValue, Is.EqualTo(increment + 1)); + } Assert.That(optimizer.GetLastSourceValue(TenantIdentifier), Is.EqualTo(increment * 2 + 1)); // initialization (2) + clock over Assert.That(optimizer.GetLastValue(TenantIdentifier), Is.EqualTo(increment + 1)); diff --git a/src/NHibernate.Test/IdGen/Enhanced/Table/HiLoTableTest.cs b/src/NHibernate.Test/IdGen/Enhanced/Table/HiLoTableTest.cs index 014788d6570..d25c772661c 100644 --- a/src/NHibernate.Test/IdGen/Enhanced/Table/HiLoTableTest.cs +++ b/src/NHibernate.Test/IdGen/Enhanced/Table/HiLoTableTest.cs @@ -44,6 +44,12 @@ public void TestNormalBoundary() entities[i] = new Entity("" + (i + 1)); s.Save(entities[i]); Assert.That(generator.TableAccessCount, Is.EqualTo(1)); // initialization + if (TenantIdentifier == null) + { + Assert.That(optimizer.LastSourceValue, Is.EqualTo(1)); // initialization + Assert.That(optimizer.LastValue, Is.EqualTo(i + 1)); + Assert.That(optimizer.HiValue, Is.EqualTo(increment + 1)); + } Assert.That(optimizer.GetLastSourceValue(TenantIdentifier), Is.EqualTo(1)); // initialization Assert.That(optimizer.GetLastValue(TenantIdentifier), Is.EqualTo(i + 1)); Assert.That(optimizer.GetHiValue(TenantIdentifier), Is.EqualTo(increment + 1)); @@ -53,6 +59,12 @@ public void TestNormalBoundary() entities[increment] = new Entity("" + increment); s.Save(entities[increment]); Assert.That(generator.TableAccessCount, Is.EqualTo(2)); + if (TenantIdentifier == null) + { + Assert.That(optimizer.LastSourceValue, Is.EqualTo(2)); + Assert.That(optimizer.LastValue, Is.EqualTo(increment + 1)); + Assert.That(optimizer.HiValue, Is.EqualTo((increment * 2) + 1)); + } Assert.That(optimizer.GetLastSourceValue(TenantIdentifier), Is.EqualTo(2)); Assert.That(optimizer.GetLastValue(TenantIdentifier), Is.EqualTo(increment + 1)); Assert.That(optimizer.GetHiValue(TenantIdentifier), Is.EqualTo((increment * 2) + 1)); diff --git a/src/NHibernate.Test/IdGen/Enhanced/Table/PooledLoTableTest.cs b/src/NHibernate.Test/IdGen/Enhanced/Table/PooledLoTableTest.cs index e56c2fed120..4181a25372e 100644 --- a/src/NHibernate.Test/IdGen/Enhanced/Table/PooledLoTableTest.cs +++ b/src/NHibernate.Test/IdGen/Enhanced/Table/PooledLoTableTest.cs @@ -44,6 +44,11 @@ public void TestNormalBoundary() entities[i] = new Entity("" + (i + 1)); s.Save(entities[i]); Assert.That(generator.TableAccessCount, Is.EqualTo(1)); // initialization + if (TenantIdentifier == null) + { + Assert.That(optimizer.LastSourceValue, Is.EqualTo(1)); // initialization + Assert.That(optimizer.LastValue, Is.EqualTo(i + 1)); + } Assert.That(optimizer.GetLastSourceValue(TenantIdentifier), Is.EqualTo(1)); // initialization Assert.That(optimizer.GetLastValue(TenantIdentifier), Is.EqualTo(i + 1)); } @@ -52,6 +57,11 @@ public void TestNormalBoundary() entities[increment] = new Entity("" + increment); s.Save(entities[increment]); Assert.That(generator.TableAccessCount, Is.EqualTo(2)); + if (TenantIdentifier == null) + { + Assert.That(optimizer.LastSourceValue, Is.EqualTo(increment + 1)); + Assert.That(optimizer.LastValue, Is.EqualTo(increment + 1)); + } Assert.That(optimizer.GetLastSourceValue(TenantIdentifier), Is.EqualTo(increment + 1)); Assert.That(optimizer.GetLastValue(TenantIdentifier), Is.EqualTo(increment + 1)); diff --git a/src/NHibernate.Test/IdGen/Enhanced/Table/PooledTableTest.cs b/src/NHibernate.Test/IdGen/Enhanced/Table/PooledTableTest.cs index 4f357bc6066..383b2925b03 100644 --- a/src/NHibernate.Test/IdGen/Enhanced/Table/PooledTableTest.cs +++ b/src/NHibernate.Test/IdGen/Enhanced/Table/PooledTableTest.cs @@ -43,6 +43,11 @@ public void TestNormalBoundary() entities[i] = new Entity("" + (i + 1)); s.Save(entities[i]); Assert.That(generator.TableAccessCount, Is.EqualTo(2)); // initialization calls seq twice + if (TenantIdentifier == null) + { + Assert.That(optimizer.LastSourceValue, Is.EqualTo(increment + 1)); // initialization calls seq twice + Assert.That(optimizer.LastValue, Is.EqualTo(i + 1)); + } Assert.That(optimizer.GetLastSourceValue(TenantIdentifier), Is.EqualTo(increment + 1)); // initialization calls seq twice Assert.That(optimizer.GetLastValue(TenantIdentifier), Is.EqualTo(i + 1)); } @@ -51,6 +56,11 @@ public void TestNormalBoundary() entities[increment] = new Entity("" + increment); s.Save(entities[increment]); Assert.That(generator.TableAccessCount, Is.EqualTo(3)); // initialization (2) + clock over + if (TenantIdentifier == null) + { + Assert.That(optimizer.LastSourceValue, Is.EqualTo((increment * 2) + 1)); // initialization (2) + clock over + Assert.That(optimizer.LastValue, Is.EqualTo(increment + 1)); + } Assert.That(optimizer.GetLastSourceValue(TenantIdentifier), Is.EqualTo((increment * 2) + 1)); // initialization (2) + clock over Assert.That(optimizer.GetLastValue(TenantIdentifier), Is.EqualTo(increment + 1)); diff --git a/src/NHibernate/Async/Id/Enhanced/OptimizerFactory.cs b/src/NHibernate/Async/Id/Enhanced/OptimizerFactory.cs index f18744e6c08..1e9350f3e94 100644 --- a/src/NHibernate/Async/Id/Enhanced/OptimizerFactory.cs +++ b/src/NHibernate/Async/Id/Enhanced/OptimizerFactory.cs @@ -9,7 +9,6 @@ using System; -using System.Collections.Concurrent; using System.Reflection; using NHibernate.Util; @@ -28,10 +27,11 @@ public partial class HiLoOptimizer : OptimizerSupport public override async Task GenerateAsync(IAccessCallback callback, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); - using (await (_asyncLock.LockAsync()).ConfigureAwait(false)) - { - var generationState = _stateStore.LocateGenerationState(callback.GetTenantIdentifier()); + var generationState = _stateStore.LocateGenerationState(callback.GetTenantIdentifier()); + cancellationToken.ThrowIfCancellationRequested(); + using (await (generationState.AsyncLock.LockAsync()).ConfigureAwait(false)) + { if (generationState.LastSourceValue < 0) { generationState.LastSourceValue = await (callback.GetNextValueAsync(cancellationToken)).ConfigureAwait(false); @@ -106,10 +106,11 @@ public partial class PooledOptimizer : OptimizerSupport, IInitialValueAwareOptim public override async Task GenerateAsync(IAccessCallback callback, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); - using (await (_asyncLock.LockAsync()).ConfigureAwait(false)) - { - var generationState = _stateStore.LocateGenerationState(callback.GetTenantIdentifier()); + var generationState = _stateStore.LocateGenerationState(callback.GetTenantIdentifier()); + cancellationToken.ThrowIfCancellationRequested(); + using (await (generationState.AsyncLock.LockAsync()).ConfigureAwait(false)) + { if (generationState.HiValue < 0) { generationState.Value = await (callback.GetNextValueAsync(cancellationToken)).ConfigureAwait(false); @@ -150,10 +151,11 @@ public partial class PooledLoOptimizer : OptimizerSupport public override async Task GenerateAsync(IAccessCallback callback, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); - using (await (_asyncLock.LockAsync()).ConfigureAwait(false)) - { - var generationState = _stateStore.LocateGenerationState(callback.GetTenantIdentifier()); + var generationState = _stateStore.LocateGenerationState(callback.GetTenantIdentifier()); + cancellationToken.ThrowIfCancellationRequested(); + using (await (generationState.AsyncLock.LockAsync()).ConfigureAwait(false)) + { if (generationState.LastSourceValue < 0 || generationState.Value >= (generationState.LastSourceValue + IncrementSize)) { generationState.LastSourceValue = await (callback.GetNextValueAsync(cancellationToken)).ConfigureAwait(false); diff --git a/src/NHibernate/Async/Id/SequenceHiLoGenerator.cs b/src/NHibernate/Async/Id/SequenceHiLoGenerator.cs index 7ed2b4a2ef4..ddf62bb98f6 100644 --- a/src/NHibernate/Async/Id/SequenceHiLoGenerator.cs +++ b/src/NHibernate/Async/Id/SequenceHiLoGenerator.cs @@ -38,10 +38,11 @@ public partial class SequenceHiLoGenerator : SequenceGenerator public override async Task GenerateAsync(ISessionImplementor session, object obj, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); - using (await (_asyncLock.LockAsync()).ConfigureAwait(false)) - { - var generationState = _stateStore.LocateGenerationState(session.GetTenantIdentifier()); + var generationState = _stateStore.LocateGenerationState(session.GetTenantIdentifier()); + cancellationToken.ThrowIfCancellationRequested(); + using (await (generationState.AsyncLock.LockAsync()).ConfigureAwait(false)) + { if (maxLo < 1) { //keep the behavior consistent even for boundary usages diff --git a/src/NHibernate/Async/Id/TableHiLoGenerator.cs b/src/NHibernate/Async/Id/TableHiLoGenerator.cs index 760556da853..8ee8ab67c50 100644 --- a/src/NHibernate/Async/Id/TableHiLoGenerator.cs +++ b/src/NHibernate/Async/Id/TableHiLoGenerator.cs @@ -37,10 +37,11 @@ public partial class TableHiLoGenerator : TableGenerator public override async Task GenerateAsync(ISessionImplementor session, object obj, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); - using (await (_asyncLock.LockAsync()).ConfigureAwait(false)) - { - var generationState = _stateStore.LocateGenerationState(session.GetTenantIdentifier()); + var generationState = _stateStore.LocateGenerationState(session.GetTenantIdentifier()); + cancellationToken.ThrowIfCancellationRequested(); + using (await (generationState.AsyncLock.LockAsync()).ConfigureAwait(false)) + { if (maxLo < 1) { //keep the behavior consistent even for boundary usages diff --git a/src/NHibernate/Id/Enhanced/OptimizerFactory.cs b/src/NHibernate/Id/Enhanced/OptimizerFactory.cs index d599ce49521..fc239a27145 100644 --- a/src/NHibernate/Id/Enhanced/OptimizerFactory.cs +++ b/src/NHibernate/Id/Enhanced/OptimizerFactory.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Concurrent; using System.Reflection; using NHibernate.Util; @@ -98,7 +97,6 @@ public static IOptimizer BuildOptimizer(string type, System.Type returnClass, in public partial class HiLoOptimizer : OptimizerSupport { - private readonly AsyncLock _asyncLock = new AsyncLock(); private readonly TenantStateStore _stateStore = new TenantStateStore(); public HiLoOptimizer(System.Type returnClass, int incrementSize) : base(returnClass, incrementSize) @@ -115,6 +113,7 @@ public HiLoOptimizer(System.Type returnClass, int incrementSize) : base(returnCl public class GenerationState { + public AsyncLock AsyncLock { get; } = new AsyncLock(); public long LastSourceValue { get; internal set; } = -1; public long Value { get; internal set; } public long UpperLimit { get; internal set; } @@ -162,10 +161,10 @@ public override bool ApplyIncrementSizeToSourceValues public override object Generate(IAccessCallback callback) { - using (_asyncLock.Lock()) - { - var generationState = _stateStore.LocateGenerationState(callback.GetTenantIdentifier()); + var generationState = _stateStore.LocateGenerationState(callback.GetTenantIdentifier()); + using (generationState.AsyncLock.Lock()) + { if (generationState.LastSourceValue < 0) { generationState.LastSourceValue = callback.GetNextValue(); @@ -290,7 +289,6 @@ protected virtual object Make(long value) public partial class PooledOptimizer : OptimizerSupport, IInitialValueAwareOptimizer { private long _initialValue; - private readonly AsyncLock _asyncLock = new AsyncLock(); private readonly TenantStateStore _stateStore = new TenantStateStore(); public PooledOptimizer(System.Type returnClass, int incrementSize) : base(returnClass, incrementSize) { @@ -306,6 +304,7 @@ public PooledOptimizer(System.Type returnClass, int incrementSize) : base(return public class GenerationState { + public AsyncLock AsyncLock { get; } = new AsyncLock(); public long Value { get; internal set; } public long HiValue { get; internal set; } = -1; } @@ -319,6 +318,11 @@ public override long LastSourceValue /// Exposure intended for testing purposes. /// /// + + public long LastValue + { + get { return _stateStore.NoTenantGenerationState.Value - 1; } + } public long GetLastSourceValue(string tenantIdentifier) { return _stateStore.LocateGenerationState(tenantIdentifier).HiValue; @@ -340,10 +344,10 @@ public void InjectInitialValue(long initialValue) public override object Generate(IAccessCallback callback) { - using (_asyncLock.Lock()) - { - var generationState = _stateStore.LocateGenerationState(callback.GetTenantIdentifier()); + var generationState = _stateStore.LocateGenerationState(callback.GetTenantIdentifier()); + using (generationState.AsyncLock.Lock()) + { if (generationState.HiValue < 0) { generationState.Value = callback.GetNextValue(); @@ -380,7 +384,6 @@ public override object Generate(IAccessCallback callback) public partial class PooledLoOptimizer : OptimizerSupport { - private readonly AsyncLock _asyncLock = new AsyncLock(); private readonly TenantStateStore _stateStore = new TenantStateStore(); public PooledLoOptimizer(System.Type returnClass, int incrementSize) : base(returnClass, incrementSize) @@ -397,16 +400,17 @@ public PooledLoOptimizer(System.Type returnClass, int incrementSize) : base(retu public class GenerationState { + public AsyncLock AsyncLock { get; } = new AsyncLock(); public long LastSourceValue { get; internal set; } = -1; public long Value { get; internal set; } } public override object Generate(IAccessCallback callback) { - using (_asyncLock.Lock()) - { - var generationState = _stateStore.LocateGenerationState(callback.GetTenantIdentifier()); + var generationState = _stateStore.LocateGenerationState(callback.GetTenantIdentifier()); + using (generationState.AsyncLock.Lock()) + { if (generationState.LastSourceValue < 0 || generationState.Value >= (generationState.LastSourceValue + IncrementSize)) { generationState.LastSourceValue = callback.GetNextValue(); diff --git a/src/NHibernate/Id/SequenceHiLoGenerator.cs b/src/NHibernate/Id/SequenceHiLoGenerator.cs index 475027dde93..ec20886f66f 100644 --- a/src/NHibernate/Id/SequenceHiLoGenerator.cs +++ b/src/NHibernate/Id/SequenceHiLoGenerator.cs @@ -46,10 +46,10 @@ public partial class SequenceHiLoGenerator : SequenceGenerator private int maxLo; private System.Type returnClass; private TenantStateStore _stateStore; - private readonly AsyncLock _asyncLock = new AsyncLock(); - + private class GenerationState { + public AsyncLock AsyncLock { get; } = new AsyncLock(); public long Lo { get; internal set; } public long Hi { get; internal set; } } @@ -84,10 +84,10 @@ public override void Configure(IType type, IDictionary parms, Di /// The new identifier as a , , or . public override object Generate(ISessionImplementor session, object obj) { - using (_asyncLock.Lock()) - { - var generationState = _stateStore.LocateGenerationState(session.GetTenantIdentifier()); + var generationState = _stateStore.LocateGenerationState(session.GetTenantIdentifier()); + using (generationState.AsyncLock.Lock()) + { if (maxLo < 1) { //keep the behavior consistent even for boundary usages diff --git a/src/NHibernate/Id/TableHiLoGenerator.cs b/src/NHibernate/Id/TableHiLoGenerator.cs index 1629fc7430e..9cf03db31ac 100644 --- a/src/NHibernate/Id/TableHiLoGenerator.cs +++ b/src/NHibernate/Id/TableHiLoGenerator.cs @@ -52,10 +52,10 @@ public partial class TableHiLoGenerator : TableGenerator private long maxLo; private System.Type returnClass; private TenantStateStore _stateStore; - private readonly AsyncLock _asyncLock = new AsyncLock(); - + private class GenerationState { + public AsyncLock AsyncLock { get; } = new AsyncLock(); public long Lo { get; internal set; } public long Hi { get; internal set; } } @@ -89,10 +89,10 @@ public override void Configure(IType type, IDictionary parms, Di /// The new identifier as a . public override object Generate(ISessionImplementor session, object obj) { - using (_asyncLock.Lock()) - { - var generationState = _stateStore.LocateGenerationState(session.GetTenantIdentifier()); + var generationState = _stateStore.LocateGenerationState(session.GetTenantIdentifier()); + using (generationState.AsyncLock.Lock()) + { if (maxLo < 1) { //keep the behavior consistent even for boundary usages diff --git a/src/NHibernate/Id/TenantStateStore.cs b/src/NHibernate/Id/TenantStateStore.cs index fbb4d8372a6..276df6a3e81 100644 --- a/src/NHibernate/Id/TenantStateStore.cs +++ b/src/NHibernate/Id/TenantStateStore.cs @@ -5,36 +5,34 @@ namespace NHibernate.Id { internal class TenantStateStore where TState : new() { - private TState _noTenantState; + private Lazy _noTenantState; private Action _initializer; - private readonly Lazy> _tenantSpecificState = new Lazy>(() => new ConcurrentDictionary()); + private ConcurrentDictionary _tenantSpecificState = new ConcurrentDictionary(); - public TenantStateStore(Action initializer) + public TenantStateStore(Action initializer) : this() { _initializer = initializer; } public TenantStateStore() { + _noTenantState = new Lazy(() => CreateNewState()); } internal TState LocateGenerationState(string tenantIdentifier) { if (tenantIdentifier == null) { - if (_noTenantState == null) - { - _noTenantState = CreateNewState(); - } - return _noTenantState; + return _noTenantState.Value; } else { - return _tenantSpecificState.Value.GetOrAdd(tenantIdentifier, _ => CreateNewState()); + return _tenantSpecificState.GetOrAdd(tenantIdentifier, _ => CreateNewState()); } } - internal TState NoTenantGenerationState => _noTenantState ?? + internal TState NoTenantGenerationState => _noTenantState.IsValueCreated ? + _noTenantState.Value : throw new HibernateException("Could not locate previous generation state for no-tenant");