5

我们的系统中有一个实体,称为“身份程序”。这也是我们的分片边界,每个身份程序都存储在自己的分片中,所以分片的标识符就是身份程序的标识符。

我们正在实施物理删除身份程序的能力。作为该过程的一部分,我们要清理分片映射。为此,我编写了以下内容:

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.spBulkOperationShardMappingsGlobalBegin”时出错。如果另一个并发用户已经删除了映射,则可能会发生这种情况。

但是映射并没有被删除,因为在那之后我执行:

mappings = shardMap.GetMappings();
foreach(var mapping in mappings)
{
    Console.WriteLine(mapping.Value);
}

而且我可以看到 shardmap 条目仍然存在,并且标记为 Offline。

如果我删除对我的调用,MarkMappingOffline我会收到一个异常,指出无法删除分片映射,因为它是在线的。

所以我似乎有一个catch-22。如果我将其标记为离线,它会认为分片映射已经消失并且不会让我删除它。如果我没有将其标记为离线,它会告诉我它必须离线。

4

1 回答 1

7

您必须始终对当前版本的映射进行操作,因此您的代码应该是

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。实际上那个“其他人”是你自己,但是因为你没有删除最新版本的映射,elastic db tools 并没有意识到是你。:)

于 2017-10-17T00:20:04.777 回答