You need to enable JavaScript to run this app. RavenDB如何正确并发更新同一文档:并行调用同一API端点仅新增单条附件元数据
优惠活动
大模型
产品
解决方案
定价
更多

RavenDB如何正确并发更新同一文档:并行调用同一API端点仅新增单条附件元数据

问题描述

我开发了一个带有upload端点的C# REST API,该端点的唯一用途是处理二进制文件,并将其元数据(以Attachment模型的形式)添加到另一个实体的List<Attachment>属性中。

正常的串行调用表现

当我在Web应用中按如下伪代码所示的顺序调用该端点时,端点可按预期工作:处理每个二进制文件,并为指定实体添加单个Attachment记录。

const attachments = [Attachment, Attachment, Attachment];

for(const attachment of attachments) {
    await this.api.upload(attachment);
}

异常的并行调用表现

但当我尝试按如下伪代码所示的并行方式上传附件时,每个二进制文件都能被正确处理,但实体中仅新增了一个Attachment元数据对象。

const attachments = [Attachment, Attachment, Attachment];

const requests = attachments.map((a) => this.api.upload(a));
await Promise.all(requests);

端点核心简化逻辑

该端点的核心逻辑大致如下(已做简化):

var attachment = new Attachment() 
{
    // 从上传的二进制表单文件中解析填充元数据
};

using (var session = Store.OpenAsyncSession())
{
    var entity = await session.LoadAsync<Entity>(entityId);

    entity.Attachments.Add(attachment);

    await session.StoreAsync(entity);                   
    await session.SaveChangesAsync();
};

问题初步定位

我怀疑问题出在端点被同时调用的并发冲突上:两个请求同时打开数据库会话,将对应实体加载到内存中,各自向内存实体副本的Attachments列表添加对应的Attachment对象,再将修改后的实体更新回数据库。最终数据库中保存的是最后完成(即耗时最长的那个)请求的结果,其他请求的修改都会被覆盖。

问题复现实验

我搭建了可复现该问题的示例,运行后可以在公共测试RavenDB实例上查看生成的实体:

  • 打开名为Hogwarts的数据库,再查看联系人Harry Potter,可以看到新增了两个附件(对应串行测试的结果)
  • 查看联系人Hermione Granger时,仅新增了一个附件(Second.txt),而预期应该新增两个附件。

核心诉求

解决这个问题的最佳方案是什么?我不希望将文件批量发送到该端点,感谢任何帮助!

附:你可能需要手动点击运行按钮运行示例。由于测试服务器会定期清空数据,如果不存在对应数据库,可以手动创建名为Hogwarts的数据库。另外由于这属于竞态条件,有时两个Attachment都能被正常添加,因此可能需要多次运行示例才能复现异常。


内容的提问来源于stack exchange,提问作者Tom Aalbers

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.31 18:15:03