Триггер иногда терпит неудачу с ошибкой дубликата ключа - PullRequest
2 голосов
/ 23 апреля 2019

Я использую экземпляр PostgreSQL RDS в AWS. По сути, существует запрос, который вставляет данные в первую таблицу, назовем ее table. Там данные могут иметь дубликаты в некоторых полях (кроме, конечно, первичного ключа).

Затем есть триггер, который обновляет другую таблицу, infotable, не допуская дубликатов.

Триггер:

CREATE TRIGGER insert_infotable AFTER INSERT ON table
    FOR EACH ROW EXECUTE PROCEDURE insert_infotable();

Соответствующая часть функции триггера выглядит следующим образом:

CREATE OR REPLACE FUNCTION insert_infotable() RETURNS trigger AS $insert_infotable$
    BEGIN   
        --some irrelevant code
        IF NOT EXISTS (SELECT * FROM infotable WHERE col1 = NEW.col1 AND col2 = NEW.col2) THEN
            INSERT INTO infotable(col1, col2, col3, col4, col5, col6) values (--some values--);
        END IF;
        RETURN NEW;
    END;
$insert_infotable$ LANGUAGE plpgsql;

Таблица infotable имеет уникальное ограничение для столбцов col1 и col2.

В целом все работает нормально, но редко, примерно один раз в 1k вставок, триггер возвращает ошибку «значение дублированного ключа нарушает уникальное ограничение« unique_col1_and_col2 »» для таблицы infotable. Этого не должно быть, поскольку в функции триггера есть часть IF NOT EXISTS.

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

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

И если да, то какой тип блокировки я должен использовать и какую таблицу я должен заблокировать - основную таблицу или вторую, которая модифицируется триггером? (или я думаю, должен ли я иметь блокировку с моим основным оператором вставки или внутри функции триггера?)

1 Ответ

2 голосов
/ 23 апреля 2019

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

Поскольку у вас есть уникальное ограничение на infotable, вы можете просто использовать

INSERT INTO infotable ...
ON CONFLICT (col1, col2) DO NOTHING;
...