我正在尝试在文档数据库上实现两阶段提交,该文档数据库使用插入数据的事务信息仅支持单阶段提交。
我了解两阶段提交背后的理论,但在实践中存在一些障碍。
假设有一个定义了唯一 ID 的集合,事务包括更新文档的多个字段。所以为了在回滚的情况下保存旧数据,我不能覆盖现有文档,而是需要添加一个新文档并用事务ID标记它:
解决方案1:
准备阶段:
- 使用更新的字段将新文档插入主集合,并用当前事务 ID 标记它。
- 在提交之前将旧文档标记为已删除,并用当前事务 ID 标记它。
提交阶段:
- 从添加的(更新的)文档中删除事务 ID。
- 删除旧文档(标记为已删除)
问题:准备阶段的第 1 部分将失败,因为我正在添加另一个具有相同唯一 ID 的文档(尽管它只是为了交易,但它仍然会失败)。
解决方案2:
准备阶段:
- 将新文档插入到具有更新字段的临时集合中,并用当前事务 ID 标记它。
- 将主集合中的旧文档标记为已删除,直到提交并使用当前事务 ID 标记它。
提交阶段:
- 将新文档从临时集合移动到主集合。
- 从主集合中删除旧文档(标记为已删除)。
问题:如果在将文档从临时集合移动到主集合时存在唯一键冲突,则提交阶段第 1 部分可能会失败。
但是,我不能允许在提交阶段发生此错误,因为为时已晚,我希望唯一键约束在准备阶段失败。
那么实施 2 阶段提交机制并避免这些问题的正确方法是什么?