Несколько вызовов извлекают элементы из общей очереди в PostgreSQL - PullRequest
1 голос
/ 10 декабря 2011

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

Произнесите таблицу, такую ​​как: ItemId, LastAttemptDateTime, AttemptCount и различные детали элемента.

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

Я использую хранимую процедуру в MS SQL, чтобы сделать это ... что-то вроде:

CREATE PROCEDURE GetNextQueueItem AS

SET NOCOUNT ON 

DECLARE @ItemId INT

UPDATE myqueue SET @ItemId=ItemId, AttemptCount=AttemptCount+1, LastAttemptDateTime=GetDate() 
WHERE ItemId=(SELECT TOP 1 ItemId 
FROM myqueue 
ORDER BY LastAttemptDateTime ASC)

SELECT ItemId, AttemptCount, and various item detail fields 
FROM myqueue 
WHERE ItemId = @ItemId

Я довольно новичок в PostgreSQL, и мне было интересно, есть ли альтернативные подходы. (TOP 1 изменится на LIMIT 1.)

Ответы [ 2 ]

1 голос
/ 10 декабря 2011

Эквивалент PostgreSQL может выглядеть следующим образом:

CREATE OR REPLACE FUNCTION get_next_queue_item()
  RETURNS SETOF myqueue AS
$BODY$
BEGIN
    RETURN QUERY
    UPDATE myqueue
    SET    attempt_count = attempt_count + 1
          ,last_attempt_ts = now()
    WHERE  item_id = (
        SELECT item_id
        FROM   myqueue 
        ORDER  BY last_attempt_ts
        LIMIT  1
        )
    RETURNING myqueue.*;

END;
$BODY$
  LANGUAGE plpgsql VOLATILE;

Основные точки

  • Вам нужен только оператор 1 , чтобы сделать все это.UPDATE может вернуть обновленную строку в той же команде с предложением RETURNING. Состояние строки после обновления.Есть способы получить состояние перед обновлением, если это необходимо.

  • Нет необходимости в каких-либо переменных.

  • Я изменил все идентификаторыв нижний регистр, который является самым чистым стилем в PostgreSQL.

  • Я переименовал ваш столбец LastAttemptDateTime в last_attempt_ts
    ts .. для "timestamp", потому что это имятипа timestamp / datetime в Postgres.

  • Как вы уже упоминали, LIMIT 1 вместо TOP 1.

  • Я использую RETURNS SETOF myqueue в качестве возвращаемого типа.
    myqueue - связанный тип строки таблицы myqueue - для каждой таблицы или представления тип строки с тем же именем автоматически создается в PostgreSQL.
    Это объявление позволяет возвращать несколько строк, но LIMIT 1 гарантирует, что оно будет только когда-либо.

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

На самом деле, вам не нужно функция plpgsql вообще.Вы можете сделать это с помощью простого оператора SQL :

UPDATE myqueue
SET    attempt_count = attempt_count + 1
      ,last_attempt_ts = now()
WHERE  item_id = (
    SELECT item_id
    FROM   myqueue 
    ORDER  BY last_attempt_ts
    LIMIT  1
    )
RETURNING myqueue.*;
1 голос
/ 10 декабря 2011

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

  1. Посмотрите на currval последовательности, если она больше или равна идентификатору max таблицы, нет ожидающих элементов.

  2. Получить nextval. Если нет элемента с совпадающим идентификатором, вернитесь к 1 (это может произойти, если вставка в таблицу завершилась неудачно).

  3. Получить строку с соответствующим идентификатором.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...