无法使用 Azure SQL Elastic Scale 删除分片映射
Cannot delete shard mapping using Azure SQL Elastic Scale
我们的系统中有一个实体叫做"identity program.",这也是我们的分片边界,每个身份程序都存储在它自己的分片中,所以分片的标识符就是身份程序的标识符。
我们正在实施物理删除身份程序的能力。作为该过程的一部分,我们要清理分片映射。为此,我编写了以下内容:
var shardKey = new Guid("E03F1DC0-5CA9-45AE-B6EC-0C90529C0062");
var connectionString = @"shard-catalog-connection-string";
var shardMapManager = ShardMapManagerFactory.GetSqlShardMapManager(connectionString, ShardMapManagerLoadPolicy.Lazy);
var shardMap = shardMapManager.GetListShardMap<Guid>("IdentityProgramIdListShardMap");
if (shardMap.TryGetMappingForKey(shardKey, out PointMapping<Guid> mapping))
{
if (mapping.Status == MappingStatus.Online)
{
shardMap.MarkMappingOffline(mapping);
}
shardMap.DeleteMapping(mapping);
}
问题是当它遇到 DeleteMapping
调用时出现异常:
ShardManagementException: Mapping referencing shard '[shard-connection-string]' in the shard map 'IdentityProgramIdListShardMap' does not exist. Error occurred while executing stored procedure '__ShardManagement.spBulkOperationShardMappingsGlobalBegin' for operation 'RemovePointMapping'. This can occur if another concurrent user has already removed the mapping.
但是映射没有被删除,因为在那之后我执行了:
mappings = shardMap.GetMappings();
foreach(var mapping in mappings)
{
Console.WriteLine(mapping.Value);
}
而且我可以看到分片映射条目仍然存在,并且标记为离线。
如果我删除对 MarkMappingOffline
的调用,我会收到一个异常,指出无法删除分片映射,因为它在线。
所以我似乎有第二十二条军规。如果我将其标记为离线,它会认为分片映射已经消失,并且不会让我删除它。如果我不将其标记为离线,它会告诉我它必须离线。
您必须始终对当前版本的映射进行操作,因此您的代码应该是
if (shardMap.TryGetMappingForKey(shardKey, out PointMapping<Guid> mapping))
{
if (mapping.Status == MappingStatus.Online)
{
// `mapping =` on next line is needed
mapping = shardMap.MarkMappingOffline(mapping);
}
shardMap.DeleteMapping(mapping);
}
进一步解释一下:弹性数据库工具使用乐观并发模型,其中每个操作都会检查您是否在每个对象的最新版本上操作。错误消息是说对映射进行了 "concurrent" 修改,即在您获得映射之后但在执行 DeleteMapping 之前,其他人同时执行了 MarkMappingOffline。其实那个 "other person" 是你自己,但是因为你没有删除最新版本的映射,elastic db tools 并不知道是你。 :)
我们的系统中有一个实体叫做"identity program.",这也是我们的分片边界,每个身份程序都存储在它自己的分片中,所以分片的标识符就是身份程序的标识符。
我们正在实施物理删除身份程序的能力。作为该过程的一部分,我们要清理分片映射。为此,我编写了以下内容:
var shardKey = new Guid("E03F1DC0-5CA9-45AE-B6EC-0C90529C0062");
var connectionString = @"shard-catalog-connection-string";
var shardMapManager = ShardMapManagerFactory.GetSqlShardMapManager(connectionString, ShardMapManagerLoadPolicy.Lazy);
var shardMap = shardMapManager.GetListShardMap<Guid>("IdentityProgramIdListShardMap");
if (shardMap.TryGetMappingForKey(shardKey, out PointMapping<Guid> mapping))
{
if (mapping.Status == MappingStatus.Online)
{
shardMap.MarkMappingOffline(mapping);
}
shardMap.DeleteMapping(mapping);
}
问题是当它遇到 DeleteMapping
调用时出现异常:
ShardManagementException: Mapping referencing shard '[shard-connection-string]' in the shard map 'IdentityProgramIdListShardMap' does not exist. Error occurred while executing stored procedure '__ShardManagement.spBulkOperationShardMappingsGlobalBegin' for operation 'RemovePointMapping'. This can occur if another concurrent user has already removed the mapping.
但是映射没有被删除,因为在那之后我执行了:
mappings = shardMap.GetMappings();
foreach(var mapping in mappings)
{
Console.WriteLine(mapping.Value);
}
而且我可以看到分片映射条目仍然存在,并且标记为离线。
如果我删除对 MarkMappingOffline
的调用,我会收到一个异常,指出无法删除分片映射,因为它在线。
所以我似乎有第二十二条军规。如果我将其标记为离线,它会认为分片映射已经消失,并且不会让我删除它。如果我不将其标记为离线,它会告诉我它必须离线。
您必须始终对当前版本的映射进行操作,因此您的代码应该是
if (shardMap.TryGetMappingForKey(shardKey, out PointMapping<Guid> mapping))
{
if (mapping.Status == MappingStatus.Online)
{
// `mapping =` on next line is needed
mapping = shardMap.MarkMappingOffline(mapping);
}
shardMap.DeleteMapping(mapping);
}
进一步解释一下:弹性数据库工具使用乐观并发模型,其中每个操作都会检查您是否在每个对象的最新版本上操作。错误消息是说对映射进行了 "concurrent" 修改,即在您获得映射之后但在执行 DeleteMapping 之前,其他人同时执行了 MarkMappingOffline。其实那个 "other person" 是你自己,但是因为你没有删除最新版本的映射,elastic db tools 并不知道是你。 :)