我们的系统中有一个实体,称为"身份程序"。这也是我们的分片边界,每个身份程序都存储在自己的分片中,因此分片的标识符就是身份程序的标识符。
我们正在实现物理删除身份程序的功能。作为这个过程的一部分,我们想要清理碎片映射。为此,我写了以下内容:
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:引用碎片映射"IdentityProgramIdListShardMap"中的碎片"[shard connection string]"的映射不存在。为操作"RemovePointMapping"执行存储过程"__ShardManagement.spBulkOperationShardMMappingsGlobalBegin"时出错。如果另一个并发用户已经删除了映射,则可能会发生这种情况。
但映射尚未删除,因为在那之后我立即执行:
mappings = shardMap.GetMappings();
foreach(var mapping in mappings)
{
Console.WriteLine(mapping.Value);
}
我可以看到shardmap条目仍然存在,并且标记为Offline。
如果我删除对MarkMappingOffline
的调用,我会得到一个异常,说明碎片映射无法删除,因为它处于联机状态。
所以我似乎有第二十二条军规。如果我将其标记为脱机,它会认为shard映射已经消失,不会让我删除它。如果我不将其标记成脱机,它就会告诉我它必须脱机。
您必须始终在映射的当前版本上操作,因此您的代码应该是
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);
}
进一步解释一下:弹性数据库工具使用了一个乐观并发模型,其中每个操作都会检查您是否在每个对象的最新版本上操作。错误消息表示对映射进行了"并发"修改,即在您获得映射之后但在执行DeleteMapping之前,其他人同时执行MarkMappingOffline。事实上,"其他人"是你自己,但因为你没有删除最新版本的映射,弹性数据库工具没有意识到是你。:)