как я могу сделать условную вставку в postgres, когда могут быть одновременные вставки, которые могут создать конфликт? - PullRequest
0 голосов
/ 13 марта 2020

Я пытаюсь написать среду экспериментов, в которой пользователь может запланировать некоторые эксперименты на основе идентификаторов местоположения и времени .

моя схема таблицы выглядит следующим образом:

TABLE experiment (
    id INT NOT NULL PRIMARY KEY,
    name varchar(20) NOT NULL,
    locationIds varchar[] NOT NULL,
    timeStart timestamp NOT NULL,
    timeEnd timestamp NOT NULL,
    createdAt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updatedAt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
)

есть операции вставки, которые должны быть выполнены с условием, что местоположение и время не должны перекрываться. Я хотел знать, что можно сделать, чтобы избежать непоследовательности состояния данных, когда есть 2 одновременных вставки, занятых в тех случаях, когда местоположение ИЛИ время перекрывается,

В идеале я хочу, чтобы одна из вставок была успешной, но я в порядке Если оба сбоя и приложение должно повторить попытку.

Немного сблизился Я попытался подумать:

Подход:

APPROACH-1

  1. Имеет столбец enable , в котором указывается, является ли определенная запись действительной ИЛИ нет.
  2. Я вставляю запись расписания эксперимента с enable = FALSE

  3. Затем я проверяю, есть ли какая-либо другая запись, которая включена и перекрывается с текущей вставкой.

  4. IF есть такая запись, тогда я ничего не делаю и этот эксперимент не запланирован. Остальное Я обновляю запись на enable = TRUE .

Проблема: если одновременно происходит конфликтующая вставка, оба получат enable = TRUE когда оба очистили шаг 3.

Я подумал, что если я позволю уровню изоляции транзакций быть незафиксированным для чтения, тогда я не смогу дифференцировать те, которые находятся в процессе а те, которые уже enable = TRUE

Тогда я подумал: если я отмечу enable как enum [IN_PROGRESS, ENABLED, DISABLED], то подход будет выглядеть следующим образом.

APPROACH-2

  1. Имеет столбец enable , который сообщает, является ли определенная запись [IN_PROGRESS, ENABLED, DISABLED]

  2. Я вставляю запись расписания эксперимента с enable = IN_PROGRESS

  3. Затем я проверяю, есть ли какая-либо другая запись, которая enable = ENABLED ИЛИ enable = IN_PROGRESS и перекрывается с текущей вставкой.

  4. * 108 4 * ЕСЛИ есть такая запись, тогда я обновляю enable = DISABLED , и этот эксперимент не запланирован. Иное Я обновляю запись до enable = ENABLED .

Проблема: если есть конфликтующая вставка одновременно, оба получат enable = DISABLED , когда оба очищают шаг-3 и получают такую ​​перекрывающуюся запись.

Если уровень изоляции транзакции равен READ-COMMITTED , тогда это будет работать только ЕСЛИ каждый шаг это транзакция, а скорее весь процесс как одна транзакция. Если уровень изоляции транзакции равен READ-UNCOMMITTED , то это может быть принято за одну транзакцию, а состояние DISABLED также может быть принято за шаг ROLLBACK.

APPROACH-3

Используя решение на основе триггера, поскольку я использую POSTGRES, я могу добавить триггер для каждой операции вставки, опубликовать вставку, где я проверяю такую ​​перекрывающуюся запись, если ее нет, затем я обновляю строка должна иметь enable = TRUE

CREATE OR REPLACE FUNCTION enable_if_unique() 
RETURNS TRIGGER AS $$
BEGIN
    IF (TG_OP = 'INSERT') THEN
    UPDATE experiment
    SET NEW.enable=true
    WHERE (SELECT count(1)
           FROM experiment
           WHERE enable= true AND location_Ids && OLD.location_ids AND (OLD.timeStart, OLD.timeEnd) OVERLAPS (timeStart, timeEnd)
        ) = 0;
    RETURN NEW;
    END IF;
END;
$$ LANGUAGE 'plpgsql';
CREATE TRIGGER enable_if_unique_trigger BEFORE INSERT ON experiment FOR EACH ROW EXECUTE PROCEDURE enable_if_unique();

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

APPROACH-4

Из онлайн-поиска другого возможного решения я вижу вставки, задействованные с помощью Выбрать Оператор и предложение WHERE, помогающие добавить требуемое условие.

INSERT INTO experiment(id, name, locationIds, timeStart, timeEnd) 
SELECT 1, 'exp-1', ARRAY[123,234,345], '2020-03-13 12:00:00' 
WHERE (
       SELECT count(1) 
       FROM EXPERIMENT 
       WHERE enable= true 
             AND 
             location_Ids && OLD.location_ids 
             AND 
             (OLD.timeStart, OLD.timeEnd) OVERLAPS (timeStart, timeEnd)
      ) = 0;

Я чувствую, что проблема согласованности все еще существует, поскольку обе параллельные операции не смогут читать каждый в операторе SELECT, проверяя ограничение.

Конечный ПОДХОД: APPROACH-2

Мне нравится знать следующее:

  1. Какой наилучший подход с точки зрения масштабируемости и высокой пропускной способности?

  2. Какой подход фактически обеспечивает поддержание согласованности данных?

  3. Любой другой подход, который я мог бы здесь использовать и пропустить !!!

Новичок ie К POSTGRES, ПРИМЕРИТ ПРИМЕР ИЛИ ССЫЛКИ

1 Ответ

0 голосов
/ 22 марта 2020

как упомянуто @ a_horse_with_no_name

мы можем использовать ограничение исключения:

-- this prevents overlaps in the locationids AND the time range
alter table experiment 
  add constraint no_overlap 
  exclude using gist (locationids with &&, tsrange(timestart, timeend) with &&);
...