You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Cosmos DB首次创建同主键文档时并发竞态致数据丢失求助

解决Cosmos DB首次创建文档时的并发竞态数据丢失问题

问题分析

你遇到的核心问题是:当两个请求同时尝试创建同主键/分区键的文档时,Cosmos DB默认的Upsert或Create操作会因无锁并发导致后执行的请求覆盖先执行的,丢失其中一条数据。常规的eTag并发控制仅适用于文档已存在的场景——首次创建时文档不存在,没有可匹配的版本标识,eTag无法生效。

可行解决方案

1. 使用Cosmos DB存储过程实现原子操作

存储过程在同一个分区键范围内是串行原子执行的,能彻底避免同分区内的并发竞态。编写存储过程实现以下逻辑:

  • 根据id和pkey查询目标文档
  • 若文档存在:将新的value元素追加到现有数组,更新文档
  • 若文档不存在:创建包含value数组的新文档

示例存储过程代码(JavaScript):

function appendValue(id, pkey, newValue) {
    var collection = getContext().getCollection();
    var response = getContext().getResponse();
    
    // 查询目标文档
    var query = "SELECT * FROM c WHERE c.id = @id AND c.pkey = @pkey";
    var parameters = [
        {name: "@id", value: id},
        {name: "@pkey", value: pkey}
    ];
    
    collection.queryDocuments(collection.getSelfLink(), query, {parameters: parameters}, function(err, docs) {
        if (err) throw err;
        
        if (docs.length > 0) {
            // 文档存在,追加value
            var doc = docs[0];
            doc.value.push(newValue);
            collection.replaceDocument(doc._self, doc, function(err, updatedDoc) {
                if (err) throw err;
                response.setBody(updatedDoc);
            });
        } else {
            // 文档不存在,创建新文档
            var newDoc = {
                id: id,
                pkey: pkey,
                value: [newValue]
            };
            collection.createDocument(collection.getSelfLink(), newDoc, function(err, createdDoc) {
                if (err) throw err;
                response.setBody(createdDoc);
            });
        }
    });
}

调用时传入对应的id、pkey和要追加的newValue即可,整个操作在分区内是原子的,不会出现数据覆盖。

2. 客户端乐观重试+版本检查(备选方案)

若不想使用存储过程,可在客户端实现重试逻辑,结合Upsert和eTag特殊处理:

  • 首次尝试Upsert文档,设置If-Match: *(允许创建或更新任何版本)
  • 若收到412 Precondition Failed(表示并发修改发生),查询最新文档,合并value数组后再次Upsert,同时带上最新的eTag
  • 重复上述步骤直到成功或达到重试上限

注:该方案依赖客户端重试逻辑,相比存储过程的原子性,存在极小的重试开销,但实现更轻量。

为什么常规eTag设置无效?

当文档不存在时,设置的eTag没有对应的文档版本可匹配——Cosmos DB仅在文档已存在时检查eTag是否匹配。因此首次创建场景下,eTag无法起到并发控制作用,必须通过原子操作或重试合并的方式解决。

内容的提问来源于stack exchange,提问作者Gokul G.K

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 20:46:12