让我们举一个简单的例子——将资源保存在两个不同的存储介质中。假设您正在管理文件上传 API,并且希望跟踪关系数据库中的文件元数据,并将实际的 Blob 内容上传到专用文件存储服务(例如 Amazon S3 或 Azure Blob 存储)中。
您的方法可能类似于:
async function uploadFile(file) {
var id = await fileStorage.uploadFile(file);
file.id = id;
await db.saveFile(file);
}
但是等等,如果第二次调用失败怎么办?如果操作是幂等的,重试策略会有所帮助,但它们仍然不能保证成功。那么我们来尝试撤销之前的操作吧。
async function uploadFile(file) {
var id = await fileStorage.uploadFile(file);
file.id = id;
try {
await db.saveFile(file);
} catch (e) {
await fileStorage.deleteFile(id);
}
}
当然可以,但是如果
deleteFile
也失败了怎么办?不能无限地重试反向操作。如果 API 崩溃并且您丢失了想要恢复的数据,甚至可能无法实现。
您可以保留某种指示已发生更改的消息,但该调用本身也可能会失败。
另一种解决方案可能是让后台任务查询这两种服务并查找不一致之处,但这可能无法很好地扩展。
这似乎是一个相对简单且常见的场景,有哪些相当有效的方法可以在无需人工干预的情况下确保两个服务之间的一致性?
不要将其视为最终一致性问题,而应将其视为状态跟踪问题。例如,您可以先将元数据保存到数据库中,并以状态指示尚未上传,然后将状态转换为指示上传成功或失败。如果您想在失败时重试,那就需要更多的状态跟踪和代码来实现您的操作。