MongoDB从多个进程并发写入/获取会导致批量写入操作错误

时间:2017-11-01 08:52:23

标签: c# asp.net mongodb concurrency web-garden

我目前正在实施一个用于缓存的MongoDB数据库。

我已经创建了一个非常通用的客户端,save方法的工作方式如下:

public virtual void SaveAndOverwriteExistingCollection<T>(string collectionKey, T[] data)
{
    if (data == null || !data.Any())
        return;

    var collection = Connector.MongoDatabase.GetCollection<T>(collectionKey.ToString());
    var filter = new FilterDefinitionBuilder<T>().Empty;

    var operations = new List<WriteModel<T>>
    {
        new DeleteManyModel<T>(filter),
    };

    operations.AddRange(data.Select(t => new InsertOneModel<T>(t)));

    try
    {
        collection.BulkWrite(operations, new BulkWriteOptions {  IsOrdered = true});
    }
    catch (MongoBulkWriteException mongoBulkWriteException)
    {
        throw mongoBulkWriteException;
    }
}

与我们的其他客户一样,调用此方法看起来与此类似:

public Person[] Get(bool bypassCache = false)
{
    Person[] people = null;

    if (!bypassCache)
        people = base.Get<Person>(DefaultCollectionKeys.People.CreateCollectionKey());

    if (people.SafeAny())
        return people;

    people = Client<IPeopleService>.Invoke(s => s.Get());
    base.SaveAndOverwriteExistingCollection(DefaultCollectionKeys.People.CreateCollectionKey(), people);

    return people;
}

在我们将数据持久保存到后端之后,我们通过调用Get方法从MongoDB重新加载缓存,并将参数传递给true。所以我们重新加载所有数据。

这适用于大多数用例。但考虑到我们如何为同一个应用程序使用Web园解决方案(多个进程),这会导致并发问题。如果我在另一个用户重新加载页面时保存并重新加载缓存,有时会抛出E11000重复键错误集合。

  

命令createIndexes失败:E11000重复键错误集合:   cache.Person index:Id_1_Name_1_Email_1 dup   key:{:1,:&#34; John Doe&#34;,:&#34; foo@bar.com" }。

考虑到这是一个运行多个IIS进程的Web园,锁定不会有多大好处。考虑到批量编写应该是线程安全的,我有点困惑。我已经研究过Upserting数据,但是将我们的客户端更改为特定类型并更新每个字段将花费太长时间,感觉就像不必要的工作。因此,我正在寻找一种非常通用的解决方案。

更新 删除了插入和删除。将其更改为ReplaceOneModel的集合。目前遇到的问题是只保留集合中的最后一个元素。

public virtual void SaveAndOverwriteExistingCollection<T>(string collectionKey, T[] data)
{
    if (data == null || !data.Any())
        return;

    var collection = Connector.MongoDatabase.GetCollection<T>(collectionKey.ToString());
    var filter = new FilterDefinitionBuilder<T>().Empty;

    var operations = new List<WriteModel<T>>();
    operations.AddRange(data.Select(t => new ReplaceOneModel<T>(filter, t) { IsUpsert = true }));

    try
    {
        collection.BulkWrite(operations, new BulkWriteOptions { IsOrdered = true });
    }
    catch (MongoBulkWriteException mongoBulkWriteException)
    {
        throw mongoBulkWriteException;
    }
}

刚刚传入了811个项目的集合,只有在执行此方法后才能在集合中找到最后一个项目。

持久存在DTO的示例:

public class TranslationSetting
{
    [BsonId(IdGenerator = typeof(GuidGenerator))]
    public object ObjectId { get; set; }

    public string LanguageCode { get; set; }

    public string SettingKey { get; set; }

    public string Text { get; set; }
}

使用此索引:

string TranslationSettings()
{
    var indexBuilder = new IndexKeysDefinitionBuilder<TranslationSetting>()
        .Ascending(_ => _.SettingKey)
        .Ascending(_ => _.LanguageCode);

    return MongoDBClient.CreateIndex(DefaultCollectionKeys.TranslationSettings, indexBuilder);
}

0 个答案:

没有答案