public void BasicScenarioRangeShardMaps() { bool success = true; string rangeShardMapName = "MultiTenantShardMap"; try { #region DeployShardMapManager // Deploy shard map manager. ShardMapManagerFactory.CreateSqlShardMapManager( Globals.ShardMapManagerConnectionString, ShardMapManagerCreateMode.ReplaceExisting); #endregion DeployShardMapManager #region GetShardMapManager // Obtain shard map manager. ShardMapManager shardMapManager = ShardMapManagerFactory.GetSqlShardMapManager( Globals.ShardMapManagerConnectionString, ShardMapManagerLoadPolicy.Lazy); #endregion GetShardMapManager #region CreateRangeShardMap // Create a single user per-tenant shard map. RangeShardMap <int> multiTenantShardMap = shardMapManager.CreateRangeShardMap <int>(rangeShardMapName); #endregion CreateRangeShardMap #region CreateShardAndRangeMapping for (int i = 0; i < ScenarioTests.s_multiTenantDBs.Length; i++) { // Create the shard. Shard s = multiTenantShardMap.CreateShard( new ShardLocation( Globals.ShardMapManagerTestsDatasourceName, ScenarioTests.s_multiTenantDBs[i])); // Create the mapping. RangeMapping <int> r = multiTenantShardMap.CreateRangeMapping( new Range <int>(i * 10, (i + 1) * 10), s); } #endregion CreateShardAndRangeMapping #region UpdateMapping // Let's add [50, 60) and map it to same shard as 23 i.e. MultiTenantDB3. RangeMapping <int> mappingFor23 = multiTenantShardMap.GetMappingForKey(23); RangeMapping <int> mappingFor50To60 = multiTenantShardMap.CreateRangeMapping( new Range <int>(50, 60), mappingFor23.Shard); Assert.IsTrue(mappingFor23.Shard.Location.Equals(mappingFor50To60.Shard.Location)); // Move [10, 20) from MultiTenantDB2 to MultiTenantDB1 RangeMapping <int> mappingToUpdate = multiTenantShardMap.GetMappingForKey(10); RangeMapping <int> mappingFor5 = multiTenantShardMap.GetMappingForKey(5); bool updateFailed = false; // Try updating that shard in the mapping without taking it offline first. try { multiTenantShardMap.UpdateMapping( mappingToUpdate, new RangeMappingUpdate { Shard = mappingFor5.Shard }); } catch (ShardManagementException smme) { Assert.AreEqual(smme.ErrorCode, ShardManagementErrorCode.MappingIsNotOffline); updateFailed = true; } Trace.Assert(updateFailed); // Mark mapping offline, update shard location. RangeMapping <int> newMappingFor10To20Offline = MarkMappingOfflineAndUpdateShard <int>( multiTenantShardMap, mappingToUpdate, mappingFor5.Shard); // Verify that update succeeded. Assert.IsTrue(newMappingFor10To20Offline.Shard.Location.Equals(mappingFor5.Shard.Location)); Assert.IsTrue(newMappingFor10To20Offline.Status == MappingStatus.Offline); // Bring the mapping back online. RangeMapping <int> newMappingFor10To20Online = multiTenantShardMap.UpdateMapping( newMappingFor10To20Offline, new RangeMappingUpdate { Status = MappingStatus.Online, }); // Verify that update succeeded. Assert.IsTrue(newMappingFor10To20Online.Status == MappingStatus.Online); #endregion UpdateMapping #region DeleteMapping // Find mapping for [0, 10). RangeMapping <int> mappingToDelete = multiTenantShardMap.GetMappingForKey(5); bool operationFailed = false; // Try to delete mapping while it is online, the delete should fail. try { multiTenantShardMap.DeleteMapping(mappingToDelete); } catch (ShardManagementException smme) { operationFailed = true; Assert.AreEqual(smme.ErrorCode, ShardManagementErrorCode.MappingIsNotOffline); } Trace.Assert(operationFailed); // The mapping must be made offline first before it can be deleted. RangeMappingUpdate ru = new RangeMappingUpdate(); ru.Status = MappingStatus.Offline; mappingToDelete = multiTenantShardMap.UpdateMapping(mappingToDelete, ru); Trace.Assert(mappingToDelete.Status == MappingStatus.Offline); multiTenantShardMap.DeleteMapping(mappingToDelete); // Verify that delete succeeded. try { RangeMapping <int> deletedMapping = multiTenantShardMap.GetMappingForKey(5); } catch (ShardManagementException smme) { Assert.AreEqual(smme.ErrorCode, ShardManagementErrorCode.MappingNotFoundForKey); } #endregion DeleteMapping #region OpenConnection without Validation using (SqlConnection conn = multiTenantShardMap.OpenConnectionForKey( 20, Globals.ShardUserConnectionString, ConnectionOptions.None)) { } #endregion OpenConnection without Validation #region OpenConnection with Validation // Use the stale state of "shardToUpdate" shard & see if validation works. bool validationFailed = false; try { using (SqlConnection conn = multiTenantShardMap.OpenConnection( mappingToDelete, Globals.ShardUserConnectionString, ConnectionOptions.Validate)) { } } catch (ShardManagementException smme) { validationFailed = true; Assert.AreEqual(smme.ErrorCode, ShardManagementErrorCode.MappingDoesNotExist); } Assert.AreEqual(validationFailed, true); #endregion OpenConnection with Validation #region OpenConnection without Validation and Empty Cache // Obtain new shard map manager instance ShardMapManager newShardMapManager = ShardMapManagerFactory.GetSqlShardMapManager( Globals.ShardMapManagerConnectionString, ShardMapManagerLoadPolicy.Lazy); // Get the Range Shard Map RangeShardMap <int> newMultiTenantShardMap = newShardMapManager.GetRangeShardMap <int>(rangeShardMapName); using (SqlConnection conn = newMultiTenantShardMap.OpenConnectionForKey( 20, Globals.ShardUserConnectionString, ConnectionOptions.None)) { } #endregion #region OpenConnection with Validation and Empty Cache // Obtain new shard map manager instance newShardMapManager = ShardMapManagerFactory.GetSqlShardMapManager( Globals.ShardMapManagerConnectionString, ShardMapManagerLoadPolicy.Lazy); // Get the Range Shard Map newMultiTenantShardMap = newShardMapManager.GetRangeShardMap <int>(rangeShardMapName); // Create a new mapping RangeMapping <int> newMappingToDelete = newMultiTenantShardMap.CreateRangeMapping( new Range <int>(70, 80), newMultiTenantShardMap.GetMappingForKey(23).Shard); // Delete the mapping newMappingToDelete = newMultiTenantShardMap.UpdateMapping( newMappingToDelete, new RangeMappingUpdate { Status = MappingStatus.Offline, }); newMultiTenantShardMap.DeleteMapping(newMappingToDelete); // Use the stale state of "shardToUpdate" shard & see if validation works. validationFailed = false; try { using (SqlConnection conn = newMultiTenantShardMap.OpenConnection( newMappingToDelete, Globals.ShardUserConnectionString, ConnectionOptions.Validate)) { } } catch (ShardManagementException smme) { validationFailed = true; Assert.AreEqual(smme.ErrorCode, ShardManagementErrorCode.MappingDoesNotExist); } Assert.AreEqual(validationFailed, true); #endregion #region OpenConnectionAsync without Validation using (SqlConnection conn = multiTenantShardMap.OpenConnectionForKeyAsync( 20, Globals.ShardUserConnectionString, ConnectionOptions.None).Result) { } #endregion #region OpenConnectionAsync with Validation // Use the stale state of "shardToUpdate" shard & see if validation works. validationFailed = false; try { using (SqlConnection conn = multiTenantShardMap.OpenConnectionAsync( mappingToDelete, Globals.ShardUserConnectionString, ConnectionOptions.Validate).Result) { } } catch (AggregateException ex) { ShardManagementException smme = ex.InnerException as ShardManagementException; if (smme != null) { validationFailed = true; Assert.AreEqual(smme.ErrorCode, ShardManagementErrorCode.MappingDoesNotExist); } } Assert.AreEqual(validationFailed, true); #endregion #region OpenConnectionAsync without Validation and Empty Cache // Obtain new shard map manager instance newShardMapManager = ShardMapManagerFactory.GetSqlShardMapManager( Globals.ShardMapManagerConnectionString, ShardMapManagerLoadPolicy.Lazy); // Get the Range Shard Map newMultiTenantShardMap = newShardMapManager.GetRangeShardMap <int>(rangeShardMapName); using (SqlConnection conn = newMultiTenantShardMap.OpenConnectionForKeyAsync( 20, Globals.ShardUserConnectionString, ConnectionOptions.None).Result) { } #endregion #region OpenConnectionAsync with Validation and Empty Cache // Obtain new shard map manager instance newShardMapManager = ShardMapManagerFactory.GetSqlShardMapManager( Globals.ShardMapManagerConnectionString, ShardMapManagerLoadPolicy.Lazy); // Get the Range Shard Map newMultiTenantShardMap = newShardMapManager.GetRangeShardMap <int>(rangeShardMapName); // Create a new mapping newMappingToDelete = newMultiTenantShardMap.CreateRangeMapping( new Range <int>(70, 80), newMultiTenantShardMap.GetMappingForKey(23).Shard); // Delete the mapping newMappingToDelete = newMultiTenantShardMap.UpdateMapping( newMappingToDelete, new RangeMappingUpdate { Status = MappingStatus.Offline, }); newMultiTenantShardMap.DeleteMapping(newMappingToDelete); // Use the stale state of "shardToUpdate" shard & see if validation works. validationFailed = false; try { using (SqlConnection conn = newMultiTenantShardMap.OpenConnectionAsync( newMappingToDelete, Globals.ShardUserConnectionString, ConnectionOptions.Validate).Result) { } } catch (AggregateException ex) { ShardManagementException smme = ex.InnerException as ShardManagementException; if (smme != null) { validationFailed = true; Assert.AreEqual(smme.ErrorCode, ShardManagementErrorCode.MappingDoesNotExist); } } Assert.AreEqual(validationFailed, true); #endregion #region GetMapping // Perform tenant lookup. This will populate the cache. for (int i = 0; i < ScenarioTests.s_multiTenantDBs.Length; i++) { RangeMapping <int> result = shardMapManager .GetRangeShardMap <int>("MultiTenantShardMap") .GetMappingForKey((i + 1) * 10); Trace.WriteLine(result.Shard.Location); if (i == 0) { // Since we moved [10,20) to database 1 earlier. Assert.IsTrue(result.Shard.Location.Database == ScenarioTests.s_multiTenantDBs[0]); } else if (i < 4) { Assert.IsTrue(result.Shard.Location.Database == ScenarioTests.s_multiTenantDBs[i + 1]); } else { Assert.IsTrue(result.Shard.Location.Database == ScenarioTests.s_multiTenantDBs[2]); } } // Perform tenant lookup. This will read from the cache. for (int i = 0; i < ScenarioTests.s_multiTenantDBs.Length; i++) { RangeMapping <int> result = shardMapManager .GetRangeShardMap <int>("MultiTenantShardMap") .GetMappingForKey((i + 1) * 10); Trace.WriteLine(result.Shard.Location); if (i == 0) { // Since we moved [10,20) to database 1 earlier. Assert.IsTrue(result.Shard.Location.Database == ScenarioTests.s_multiTenantDBs[0]); } else if (i < 4) { Assert.IsTrue(result.Shard.Location.Database == ScenarioTests.s_multiTenantDBs[i + 1]); } else { Assert.IsTrue(result.Shard.Location.Database == ScenarioTests.s_multiTenantDBs[2]); } } #endregion GetMapping #region Split/Merge int splitPoint = 55; // Split [50, 60) into [50, 55) and [55, 60) RangeMapping <int> mappingToSplit = multiTenantShardMap.GetMappingForKey(splitPoint); IReadOnlyList <RangeMapping <int> > rangesAfterSplit = multiTenantShardMap.SplitMapping(mappingToSplit, splitPoint); rangesAfterSplit = rangesAfterSplit.OrderBy(nr => nr.Value.Low).ToArray(); // We should get 2 ranges back. Assert.AreEqual(2, rangesAfterSplit.Count); Assert.AreEqual(rangesAfterSplit[0].Value.Low, new Range <int>(50, 55).Low); Assert.AreEqual(rangesAfterSplit[0].Value.High, new Range <int>(50, 55).High); Assert.AreEqual(rangesAfterSplit[1].Value.Low, new Range <int>(55, 60).Low); Assert.AreEqual(rangesAfterSplit[1].Value.High, new Range <int>(55, 60).High); // Split [50, 55) into [50, 52) and [52, 55) IReadOnlyList <RangeMapping <int> > newRangesAfterAdd = multiTenantShardMap.SplitMapping(rangesAfterSplit[0], 52); newRangesAfterAdd = newRangesAfterAdd.OrderBy(nr => nr.Value.Low).ToArray(); // We should get 2 ranges back. Assert.AreEqual(2, newRangesAfterAdd.Count); Assert.AreEqual(newRangesAfterAdd[0].Value.Low, new Range <int>(50, 52).Low); Assert.AreEqual(newRangesAfterAdd[0].Value.High, new Range <int>(50, 52).High); Assert.AreEqual(newRangesAfterAdd[1].Value.Low, new Range <int>(52, 55).Low); Assert.AreEqual(newRangesAfterAdd[1].Value.High, new Range <int>(52, 55).High); // Move [50, 52) to MultiTenantDB1 Shard targetShard = multiTenantShardMap.GetShard(new ShardLocation(Globals.ShardMapManagerTestsDatasourceName, ScenarioTests.s_multiTenantDBs[0])); // Mark mapping offline, update shard location. RangeMapping <int> movedMapping1 = MarkMappingOfflineAndUpdateShard <int>( multiTenantShardMap, newRangesAfterAdd[0], targetShard); // Bring the mapping back online. movedMapping1 = multiTenantShardMap.UpdateMapping( movedMapping1, new RangeMappingUpdate { Status = MappingStatus.Online, }); // Mark mapping offline, update shard location. RangeMapping <int> movedMapping2 = MarkMappingOfflineAndUpdateShard <int>( multiTenantShardMap, newRangesAfterAdd[1], targetShard); // Bring the mapping back online. movedMapping2 = multiTenantShardMap.UpdateMapping( movedMapping2, new RangeMappingUpdate { Status = MappingStatus.Online, }); // Obtain the final moved mapping. RangeMapping <int> finalMovedMapping = multiTenantShardMap.MergeMappings(movedMapping1, movedMapping2); Assert.AreEqual(finalMovedMapping.Value.Low, new Range <int>(50, 55).Low); Assert.AreEqual(finalMovedMapping.Value.High, new Range <int>(50, 55).High); #endregion Split/Merge } catch (ShardManagementException smme) { success = false; Trace.WriteLine(String.Format("Error Category: {0}", smme.ErrorCategory)); Trace.WriteLine(String.Format("Error Code : {0}", smme.ErrorCode)); Trace.WriteLine(String.Format("Error Message : {0}", smme.Message)); if (smme.InnerException != null) { Trace.WriteLine(String.Format("Storage Error Message : {0}", smme.InnerException.Message)); if (smme.InnerException.InnerException != null) { Trace.WriteLine(String.Format("SqlClient Error Message : {0}", smme.InnerException.InnerException.Message)); } } } Assert.IsTrue(success); }
public static void ShardMapManagerLoadTestsInitialize(TestContext testContext) { // Clear all connection pools. SqlConnection.ClearAllPools(); using (SqlConnection conn = new SqlConnection(Globals.ShardMapManagerTestConnectionString)) { conn.Open(); // Create ShardMapManager database using (SqlCommand cmd = new SqlCommand( string.Format(Globals.CreateDatabaseQuery, Globals.ShardMapManagerDatabaseName), conn)) { cmd.ExecuteNonQuery(); } // Create shard databases for (int i = 0; i < ShardMapManagerLoadTests.s_shardedDBs.Length; i++) { using (SqlCommand cmd = new SqlCommand( string.Format(Globals.CreateDatabaseQuery, ShardMapManagerLoadTests.s_shardedDBs[i]), conn)) { cmd.ExecuteNonQuery(); } } // cleanup for deadlock monitoring foreach (string q in s_deadlockDetectionCleanupQueries) { using (SqlCommand cmd = new SqlCommand(q, conn)) { try { cmd.ExecuteNonQuery(); } catch (SqlException) { } } } // setup for deadlock monitoring foreach (string q in s_deadlockDetectionSetupQueries) { using (SqlCommand cmd = new SqlCommand(q, conn)) { try { cmd.ExecuteNonQuery(); } catch (SqlException) { } } } } // Create shard map manager. ShardMapManagerFactory.CreateSqlShardMapManager( Globals.ShardMapManagerConnectionString, ShardMapManagerCreateMode.ReplaceExisting); // Create list shard map. ShardMapManager smm = ShardMapManagerFactory.GetSqlShardMapManager( Globals.ShardMapManagerConnectionString, ShardMapManagerLoadPolicy.Lazy); ListShardMap <int> lsm = smm.CreateListShardMap <int>(ShardMapManagerLoadTests.s_listShardMapName); Assert.IsNotNull(lsm); // Create range shard map. RangeShardMap <int> rsm = smm.CreateRangeShardMap <int>(ShardMapManagerLoadTests.s_rangeShardMapName); Assert.IsNotNull(rsm); // Add 'InitialShardCount' shards to list and range shard map. for (int i = 0; i < ShardMapManagerLoadTests.InitialShardCount; i++) { ShardCreationInfo si = new ShardCreationInfo( new ShardLocation(Globals.ShardMapManagerTestsDatasourceName, ShardMapManagerLoadTests.s_shardedDBs[i]), ShardStatus.Online); Shard sList = lsm.CreateShard(si); Assert.IsNotNull(sList); Shard sRange = rsm.CreateShard(si); Assert.IsNotNull(sRange); } // Initialize retry policy s_retryPolicy = new RetryPolicy <SqlAzureTransientErrorDetectionStrategy>( new ExponentialBackoff(5, TimeSpan.FromMilliseconds(100), TimeSpan.FromSeconds(5), TimeSpan.FromMilliseconds(100))); }
public void TestLockingFixInVersion1_2() { // Get shard map manager ShardMapManager smm = ShardMapManagerFactory.GetSqlShardMapManager( Globals.ShardMapManagerConnectionString, ShardMapManagerLoadPolicy.Lazy); // Upgrade to version 1.1 smm.UpgradeGlobalStore(new Version(1, 1)); // Create a range shard map and add few mappings RangeShardMap <int> rsm = smm.CreateRangeShardMap <int>(ShardMapManagerUpgradeTests.s_shardMapNames[1]); Assert.IsNotNull(rsm); Shard s = rsm.CreateShard(new ShardLocation(Globals.ShardMapManagerTestsDatasourceName, ShardMapManagerUpgradeTests.s_shardedDBs[0])); Assert.IsNotNull(s); RangeMapping <int> m1 = rsm.CreateRangeMapping(new Range <int>(1, 10), s); RangeMapping <int> m2 = rsm.CreateRangeMapping(new Range <int>(10, 20), s); RangeMapping <int> m3 = rsm.CreateRangeMapping(new Range <int>(20, 30), s); // Lock first 2 mappings with same lockownerid and third with a different lock owner id MappingLockToken t1 = MappingLockToken.Create(); MappingLockToken t2 = MappingLockToken.Create(); rsm.LockMapping(m1, t1); rsm.LockMapping(m2, t1); rsm.LockMapping(m3, t2); // now try to unlock using token t2. In store version 1.1 it will unlock all mappings rsm.UnlockMapping(t2); foreach (RangeMapping <int> m in rsm.GetMappings()) { Assert.AreEqual(MappingLockToken.NoLock, rsm.GetMappingLockOwner(m)); } // Now upgrade to version 1.2 and try same scenario above. smm.UpgradeGlobalStore(new Version(1, 2)); rsm.LockMapping(m1, t1); rsm.LockMapping(m2, t1); rsm.LockMapping(m3, t2); // Unlock using token t1. It should just unlock 2 mappings and leave last one locked. rsm.UnlockMapping(t1); Assert.AreEqual(MappingLockToken.NoLock, rsm.GetMappingLockOwner(rsm.GetMappingForKey(5))); Assert.AreEqual(MappingLockToken.NoLock, rsm.GetMappingLockOwner(rsm.GetMappingForKey(15))); Assert.AreEqual(t2, rsm.GetMappingLockOwner(rsm.GetMappingForKey(25))); // Cleanup - Delete all mappings. shard will be removed in test cleanup. rsm.UnlockMapping(t2); RangeMappingUpdate ru = new RangeMappingUpdate(); ru.Status = MappingStatus.Offline; foreach (RangeMapping <int> m in rsm.GetMappings()) { rsm.DeleteMapping(rsm.UpdateMapping(m, ru)); } }