0

Я думаю о состоянии гонки в производственной системе, над которой я работаю. База данных - PostgreSQL. Приложение написано на Java, но это не имеет значения.Что произойдет, если два процесса одновременно изменят данные в двух транзакциях и есть уникальное ограничение на таблицу?

Существует таблица под названием «версии», которая содержит столбцы «entity_ID» и «version» (и некоторые другие поля). Эта таблица содержит версии определенного объекта.

Существует приложение, в котором пользователь может изменять эти объекты.

Каждая модификация объекта создает новую версию в «версии» (с помощью триггера). Этот триггер находит последнюю версию в той же таблице «версии» и вставляет новую строку с тем же именем entity_ID, но version = (последняя версия + 1).

Выполняется ночная работа, выполняемая в PostgreSQL каждые 4:00, которая также изменяет эти объекты и, следовательно, обновляет данные в таблице «версии». Эта процедура была разработана для завершения работы к утру (до того, как пользователи приложения начнут ее использовать), но, к сожалению, работает в этот день. Поскольку эта процедура выполняется в функции, то это одна большая транзакция. Поэтому сделанные им изменения не отображаются в приложении.

Ночная работа использует следующие процесса:

  • Set "failed_counter" = 0
  • перебирать лиц, которые должны быть изменены
  • ли изменения в сущности внутри НАЧАТЬ .. ИСКЛЮЧЕНИЕ .. END block
  • Если есть ИСКЛЮЧЕНИЕ, увеличьте «failed_counter». Запишите исключение и поврежденный объект в таблицу журналов.
  • Если «failed_counter»> 10, отмените работу.
  • Конец работы

Это вызвало следующее состояние гонки случаться несколько раз (предположим, что X является последней версией объекта A):

  1. Nightly работа начинается
  2. Ночное задание изменяет сущность A, создавая версию X + 1
  3. Приложение также используется для модификации сущности A, создавая также версию X + 1 (поскольку ночная транзакция сделки не COMMITed, поэтому версия X + 1 не отображается в приложении)
  4. Ночная работа заканчивается, в результате чего COMMIT
  5. В настоящее время существует две версии с номером версии X + 1, что приводит к разрыву приложения.

Я думал, что могу просто решить проблему, используя UNIQUE CONSTRAINT поверх полей (entity_ID, версия). Я думал, что это приведет к тому, что приложение получит ошибку (из-за нарушения UNIQUE CONSTRAINT) в шаге 3 гонки. Но я не уверен, как уникальное ограничение работает в этой ситуации. В шаге 3 условия гонки, когда приложение добавляет версию, проверяет ли база данных UNIQUE CONSTRAINT? Я полагаю, что нет, поскольку транзакция ночного процесса еще не завершена.Если я прав, и UNIQUE CONSTRAINT проверяется только на этапе 4 этапа гонки, когда COMMIT сделан, то это приводит к сбою всей ночной процедуры, что нежелательно.

Итак, вопрос в следующем.

  • Когда проверяется УНИКАЛЬНОЕ КОНСТРАИРОВАНИЕ: В шаге 3 состояния гонки или состоянии гонки 4?
  • Если ответ на последний вопрос «Состояние гонки 4», то как я могу изменить дизайн системы, чтобы избежать вышеупомянутых проблем?
+0

Я не думаю, что вы когда-нибудь нарушите ограничение. (2) Работа изменяет A, которая блокирует запись. (3) Приложение пытается изменить A, но не может получить блокировку, поэтому она ждет. (4) Задания совершаются, блокировка освобождается. (5) Приложение получает блокировку, обновляет ее и создает версию X + 2. –

+0

Извините, я слишком упростил ситуацию в своем вопросе. Но я протестировал и увидел, что шаг (3) заставляет приложение ждать блокировки IF и ТОЛЬКО, если будет нарушено уникальное ограничение. Итак, вы правы в этом аспекте. –

ответ

3

По умолчанию уникальные ограничения в PostgreSQL проверяются в конце каждого оператора. Легко проверить поведение с помощью psql.

Некоторые большие красные флаги. , ,

Поскольку эта процедура выполняется в функции, то это одна большая транзакция.

Это не одна, большая транзакция, потому что вы используете функцию. Это одна, большая транзакция, потому что вы не запускали функцию несколько раз по более мелким подмножествам данных. Является ли вы может запустить функцию над подмножествами зависит от приложения.

перебирать лиц, которые должны быть изменены

Грубое эмпирическое правило для баз данных SQL: итерации всегда ошибка.

SQL - это ориентированный на набор язык. Работа с наборами обычно быстрее, чем итерация, и часто на несколько порядков.

Если «failed_counter»> 10, отмените работу.

Это выглядит подозрительно. Почему девять неудач в порядке? Почему у какие-либо ошибки?

Я думал, что могу просто решить проблему, используя UNIQUE CONSTRAINT поверх полей (entity_ID, версия).

, что вы не уже имеют уникальное ограничение на этих двух столбцов является большой, размахивая красным флагом. Исправьте это первым.

Тот факт, что приложение, по-видимому, ожидает завершения пакетного задания, но не ждет, может быть или не быть проблемой системного дизайна. (Это пахнет проблемы дизайна системы.)

Существует ночной работу, выполняемую в PostgreSQL каждый 4:00 ...

Вы думаете начать в 3:00?

Проверьте это, но не на вашем производственном сервере.

  • Падающий курок.
  • Добавить столбец типа timestamp with time zone.
  • Установите значение по умолчанию этого столбца. Большинство приложений будет использовать current_timestamp, но вы можете хотите clock_timestamp() вместо этого. Docs
  • Добавьте уникальное ограничение на {entity_id, новый столбец отметки времени}.

Устранение триггера может ускорить работу, достаточную для вас.

+0

Вы делаете несколько хороших пунктов. Выполнение функции в виде небольших частей будет вариантом; однако, я думаю, я предпочел бы избежать такой перезаписи системы. Замена итерации наборами также является хорошим советом; однако из-за сложности действий, которые эта функция делает невозможной (я упростил ситуацию, задав этот вопрос). Небольшое количество сбоев в порядке, потому что функция зависит от внешней системы, которая иногда может быть отключена. Предоставление 10 предметов не является компромиссом. Эти 10 предметов снова будут проверены на следующий день. Начиная с раннего времени помогает немного, но не так много. –

+0

Я просто перейду с полями UNIQUE CONSTRAINT поверх (entity_ID, версия). Я отметил Ваш ответ как принятый. –