MongoDB Параллельная запись / выборка из нескольких процессов вызывает ошибку операции массовой записи
В настоящее время я внедряю базу данных MongoDB для кэширования.
Я сделал очень общий клиент с методом сохранения, работающим так:
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;
}
После того, как мы сохранили данные в бэкэнде, мы перезагружаем кеш из MongoDB, вызывая наши методы Get, передавая аргумент true. Таким образом, мы перезагружаем все данные.
Это прекрасно работает для большинства случаев использования. Но, учитывая то, как мы используем решение Web-Garden (несколько процессов) для одного и того же приложения, это приводит к проблемам параллелизма. Если я сохраняю и перезагружаю кеш, когда другой пользователь перезагружает страницу, иногда он генерирует коллекцию ошибок дублированного ключа E11000.
Команда createIndexes завершилась неудачно: E11000 Коллекция повторяющихся ошибок: кеш. Индекс личности: Id_1_Name_1_Email_1 dup ключ: {: 1,: "Джон Доу",: "foo@bar.com" }.
Учитывая, что это веб-сад с несколькими процессами IIS, блокировка не принесет много пользы. Учитывая, как массовые записи должны быть безопасными для потоков, я немного озадачен. Я посмотрел в 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);
}