PostgreSQL: Можно ли ограничить вставки для каждого пользователя на основе разницы во времени между столбцом метки времени и текущим временем?

#node.js #postgresql

#node.js #postgresql

Вопрос:

У меня возникла проблема, когда два почти одновременных запроса (разница в — 10 мс) одного и того же пользователя (непреднамеренно дублированные на стороне клиента) успешно дважды выполняют всю логику прецедентов. Я действительно не могу решить эту ситуацию в коде моего API, поэтому я думал о том, как ограничить user_id возможность вставки строки в таблицу order максимум раз в секунду, например.

Я хочу добиться этого: если в таблице order существует строка с user_id X и эта строка была создана (вставлена) менее 1 секунды назад, вставка с user_id X завершится ошибкой.

Это может быть эффективным способом избежать непреднамеренного дублирования запросов со стороны клиента. Потому что я не могу представить ситуацию, когда пользователь мог бы отправить два сложных запроса менее чем за 1 секунду между намеренно. Меня также интересуют любые другие идеи, например, как правильно справляться с подобными ситуациями в API.

Комментарии:

1. Какая колонка, похожая на время, у вас уже есть?

2. Столбец @ row_created jjanes, который имеет тип timestamp и значение по умолчанию now()

Ответ №1:

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

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

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

Комментарии:

1. Это очень хороший момент, я об этом не подумал. Проблема с вашим решением заключается в том, что в настоящее время я не могу изменить интерфейс (у меня нет доступа к нему), поэтому я думал о том, чтобы разобраться с этим на бэкэнде. Я собираюсь искать возможное решение, но если я не смогу его решить, я попытаюсь предложить изменение интерфейса. Спасибо

Ответ №2:

Вы можете сделать это с ограничением ИСКЛЮЧЕНИЯ. Вам нужно создать свою собственную неизменяемую вспомогательную функцию и использовать расширение.

 create extension btree_gist; create function addsec(timestamptz) returns tstzrange immutable language sql as $   select tstzrange($1,$1 interval '1 second') $; create table orders (  userid int,   t timestamptz,   exclude using gist (userid with =, addsec(t) with amp;amp;) );  

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

Обратите внимание, что ограничения ИСКЛЮЧЕНИЯ могут быть гораздо менее эффективными, чем ограничения УНИКАЛЬНОСТИ. Кроме того, я не на 100% уверен, что addsec действительно неизменен. Там могут быть странные вещи с високосными секундами или что-то такое, что все портит.

Комментарии:

1. Если это не очень эффективно, то я, вероятно, не буду его использовать, потому что таблица уже большая, и с конкретной таблицей уже были некоторые проблемы с производительностью. Спасибо за предложение решения с использованием ограничения EXCLUDE, потому что у меня пока нет никакого опыта работы с ним, и это выглядит как очень полезная функция. Я поищу дополнительную информацию.