Ошибка MongoDB BulkWrite ExceededTimeLimit в .Net

Я пытаюсь отправить около 150 тысяч обновлений в базу данных Mongo (v 4.2.9, работающая в Windows, реплика этапа с двумя узлами), используя BulkWrite для драйвера С# (v2.11.6), и похоже, что это невозможно. Проект .Net Framework 4.7.2.

Документация драйвера Mongo С# ужасна, но каким-то образом на форумах и с большим количеством поиска в Google я, наконец, смог найти способ, как запустить около 150 000 обновлений с помощью пакета, примерно так (немного упрощенно для SO):

client = new MongoClient(connString);
database = client.GetDatabase(db);

// Build all the updates
List<UpdateOneModel<GroupEntry>> updates = new List<UpdateOneModel<GroupEntry>>();
foreach (GroupEntry groupEntry in stats)
{
  FilterDefinition<GroupEntry> filter = Builders<GroupEntry>.Filter.Eq(e => e.Key, groupEntry.Key);

  UpdateDefinitionBuilder<GroupEntry> update = Builders<GroupEntry>.Update;
  var groupEntrySubUpdates = new List<UpdateDefinition<GroupEntry>>();

  if (groupEntry.Value.Clicks != 0)
    groupEntrySubUpdates.Add(update.Inc(u => u.Value.Clicks, groupEntry.Value.Clicks));

  if (groupEntry.Value.Position != 0)
    groupEntrySubUpdates.Add(update.Set(u => u.Value.Position, groupEntry.Value.Position));

  UpdateOneModel<GroupEntry> groupEntryUpdate = new UpdateOneModel<GroupEntry>(filter, update.Combine(updates));
  groupEntryUpdate.IsUpsert = true;

  updates.Add(groupEntryUpdate);
}

// Now BulkWrite them in transaction to make sure data are consistent
IClientSessionHandle session = client.StartSession();
session.StartTransaction();

IMongoCollection<GroupEntry> collection = database.GetCollection<GroupEntry>(collectionName);

// Following line FAILS after some time
BulkWriteResult<GroupEntry> bulkWriteResult = collection.BulkWrite(session, updates);

if (!bulkWriteResult.IsAcknowledged)
  throw new Exception("Mongo BulkWrite is not acknowledged!");

session.CommitTransaction();

Проблема в том, что я продолжаю получать следующее исключение:

{
   "operationTime":Timestamp(1612737199,
   1),
   "ok":0.0,
   "errmsg":"Exec error resulting in state FAILURE :: caused by :: operation was interrupted",
   "code":262,
   "codeName":"ExceededTimeLimit",
   "$clusterTime":{
      "clusterTime":Timestamp(1612737199,
      1),
      "signature":{
         "hash":new BinData(0,
         "ljcwS5Gf2JBpEu/OgPFbvRqclLw="")",
         "keyId":"NumberLong(""6890288652832735234"")"
      }
   }
}

Кто-нибудь знает? Документы драйвера Mongo С# совершенно бесполезны. Похоже, мне нужно каким-то образом установить свойство $maxTimeMS, но это невозможно в BulkInsert. Я пытался:

  • Перезагружается и перестраивается
  • Различные версии MongoDriver
  • Установите гораздо большие тайм-ауты для всех свойств тайм-аута в MongoClient и сеансе.
  • Создавайте небольшие партии для массовой записи (до 1000 элементов в партии). Сбой после 50-100 обновлений.
  • Провел часы и часы в бесполезных документах Mongo и Mongo JIRA

Пока не повезло. Самое смешное, что тот же подход работает с драйвером С# 2.10.3 на .Net CORE 3.1 (да, я пробовал) даже с большими партиями (около 300 тысяч обновлений).

Что мне не хватает?

ИЗМЕНИТЬ:

Я попытался установить maxCommitTime на 25 минут, основываясь на комментариях dododo, например:

IClientSessionHandle session = client.StartSession(new ClientSessionOptions()
{
  DefaultTransactionOptions = new TransactionOptions(new Optional<ReadConcern>(ReadConcern.Default), 
    new Optional<ReadPreference>(ReadPreference.Primary), 
    new Optional<WriteConcern>(WriteConcern.Acknowledged), 
    new Optional<TimeSpan?>(TimeSpan.FromMinutes(25)))
});

Теперь при фиксации выдается исключение: NoSuchTransaction — транзакция 1 была прервана. Мы проверили лог-файл MongoDB и обнаружили там новую ошибку:

Прерывание транзакции с txnNumber 1 в сеансе 09ea7755-7148-43e8-83d8-8bf58c211bda, поскольку он выполняется дольше, чем «transactionLifetimeLimitSeconds».

Согласно документам, по умолчанию это 60 секунд. Итак, мы установили его на 5 минут, и теперь он работает.

Итак, спасибо, дододо, за то, что указал мне правильное направление.

В любом случае, было бы действительно здорово, если бы команда Mongo лучше описывала ошибки и писала документацию выше базовых операций CRUD.


person Martin Brabec    schedule 07.02.2021    source источник
comment
это ошибка сервера (не драйвера .NET) и похоже, что maxTimeMS работает только с курсорами. Я подозреваю, что это поведение транзакции, а не массовые операции, поэтому, предположительно, ваша операция работает без сеанса. Если это так, попробуйте установить maxCommitTime в параметрах транзакции.   -  person dododo    schedule 08.02.2021
comment
that the same approach works on c# driver 2.10.3 on .Net CORE 3.1 - это выглядит очень подозрительно, можете сделать еще попытки? Например, проверить этот случай еще раз для той же версии базы данных, той же структуры данных/записей с одинаковыми индексами как для net framework, так и для net core для 2.10.3 и 2.11.6?   -  person dododo    schedule 08.02.2021
comment
Даже если вы испортите документацию, вы все равно должны включить соответствующую информацию, которую вы нашли в ней.   -  person D. SM    schedule 08.02.2021
comment
dododo Я попробовал вашу рекомендацию и соответственно обновил вопрос. Спасибо. Что касается .Net Core 3.1 - я не могу сделать то, что вы предлагаете (оболочка stage/prod), но, судя по разрешению, похоже, что производство выполняется достаточно быстро, чтобы завершить каждую транзакцию за 60 секунд. Именно поэтому там это работает. Д. Д.М.: Я добавил ссылку на страницу, где описан этот подход.   -  person Martin Brabec    schedule 08.02.2021
comment
Теперь он работает, как и ожидалось. Так что еще раз спасибо, дододо.   -  person Martin Brabec    schedule 08.02.2021


Ответы (1)


Как предположил Дододо, эта ошибка была проявлением того, что сервер закрыл транзакцию, потому что это заняло больше времени, чем transactionLifetimeLimitSeconds, что по умолчанию составляет 60 секунд. Итак, необходимо сделать две вещи:

  1. Установите для параметра transactionLifetimeLimitSeconds значение более 60 секунд.
  2. Установите для параметра maxCommitTime более высокое значение. Я не могу найти значение по умолчанию, поэтому я установил его на 10 минут (так же, как transactionLifetimeLimitSeconds). Установите его при запуске сеанса (см. вопрос).

В любом случае документация для этого отсутствует, а сама ошибка вводит в заблуждение. Так что я надеюсь, что это поможет любому, кто будет иметь дело с этим.

person Martin Brabec    schedule 08.02.2021