Общая стратегия обработки одновременных глобальных обновлений инвентаря - PullRequest
0 голосов
/ 27 апреля 2019

Чтобы привести упрощенный пример:

У меня есть база данных с одной таблицей: имена, в которой содержится 1 миллион записей, каждая из которых содержит общее имя мальчика или девочки, и каждый день добавляются новые.

У меня есть сервер приложений, который принимает в качестве входных данных запрос http от родителей, используя мой веб-сайт «Выбор имени».При каждом запросе мне нужно выбрать имя из базы данных и вернуть его, а затем НЕ давать это имя другому родителю.Сервер является одновременным, поэтому может обрабатывать большое количество запросов, но при этом должен учитывать «уникальное имя на запрос» и быть по-прежнему доступным.

Каковы основные компоненты и стратегии для архитектуры этого варианта использования

1 Ответ

1 голос
/ 27 апреля 2019

Насколько я понимаю, у вас есть две операции: Добавление имени и Выбор имени .

У меня есть пара вопросов:

  • Вопрос 1: Родители выбирают только имена или они тоже добавляют имена ?

  • Вопрос 2 Если они добавляют имена , то это означает, что когда добавляется имя , оно также должно быть помечено как уже выбран

Предполагая, что вы не хотите, чтобы все запросы на выбор имени ждали друг друга (путем блокировки их очереди):

Одним из решений для разрешения параллелизма в случае выбора имени является только использование Оптимистичная автономная блокировка .

Наиболее распространенной реализацией этого является добавление поля версии в вашу таблицу и увеличение этой версии, когда вы помечаете имя как selected . Для этого вам потребуется поддержка БД, но большинство баз данных предлагают механизм для этого. MongoDB добавляет поле версии к документам по умолчанию. Для СУБД (например, SQL) вы должны добавить это поле самостоятельно.

Вы не указали, какую технологию вы используете, поэтому я приведу пример использования псевдокода для БД SQL. Для MongoDB вы можете проверить, как БД выполняет эти проверки для вас.

NameRecord {
  id,
  name,
  parentID,
  version,
  isChosen,

  function chooseForParent(parentID) {

    if(this.isChosen){
      throw Error/Exception;
    }

    this.parentID = parentID
    this.isChosen = true;
    this.version++;
  }
}

NameRecordRepository {

  function getByName(name) { ... }

  function save(record) {

    var oldVersion = record.version - 1;

    var query = "UPDATE records SET ..... 
                 WHERE id = {record.id} AND version = {oldVersion}";

    var rowsCount = db.execute(query);

    if(rowsCount == 0) {
      throw ConcurrencyViolation
    }
  }
}

// somewhere else in an object or module or whatever...

function chooseName(parentID, name) {

   var record = NameRecordRepository.getByName(name);

   record.chooseForParent(parentID);

   NameRecordRepository.save(record);
}

Перед сохранением объекта в БД необходимо выполнить сравнение версий. SQL предоставляет способ выполнить запрос, основанный на некотором условии, и вернуть количество строк затронутых строк. В нашем случае перед обновлением мы проверяем, совпадает ли версия в базе данных со старой. Если это не так, это означает, что кто-то еще обновил запись.

В этом простом случае вы даже можете удалить поле версии и использовать флаг isChosen в своем запросе SQL, например:

    var query = "UPDATE records SET ..... 
                 WHERE id = {record.id} AND isChosend = false";

При добавлении нового имени в базу данных вам понадобится уникальная константа, которая будет решать проблемы одновременности.

...