Skip to content
Search

Supabase:Queues

lastmod 2026-03-16created 2026-02-222 edits

Durable Message Queues with Guaranteed Delivery in Postgres

Replacing Redis MQ with Supabase Queues

Key point: pop() already exists

The pgmq extension has a built-in pop() equivalent to ReadAndRemove.

-- 메시지 1개를 읽고 즉시 삭제 (at-most-once)
SELECT * FROM pgmq.pop('my_queue');

Also available in the Supabase Queues public API:

SELECT * FROM pgmq_public.pop('my_queue');

WARNING

pop() has at-most-once semantics. It deletes the message as soon as it is read, so if the consumer fails during processing, the message is lost.

Comparison by pattern

Redis pattern

pgmq equivalent

Description

BRPOP (blocking pop)

pgmq.read_with_poll()

Waits up to N seconds if the queue is empty

RPOPLPUSH (pop)

pgmq.pop()

Reads and deletes immediately

LPUSH

pgmq.send()

Sends a message

visibility timeout

pgmq.read(vt)

After reading, hidden from other consumers for the VT

Building an RPC with a timeout

To "wait until a message arrives" like Redis BRPOP, use read_with_poll.

SELECT * FROM pgmq.read_with_poll(
  'my_queue',        -- queue_name
  30,                -- vt: visibility timeout (초)
  1,                 -- qty: 가져올 메시지 수
  10,                -- max_poll_seconds: 최대 대기 시간 (기본 5초)
  250                -- poll_interval_ms: 폴링 간격 (기본 100ms)
);

RPC function example

CREATE OR REPLACE FUNCTION public.dequeue_task(
  p_queue text,
  p_timeout_seconds int DEFAULT 10
)
RETURNS jsonb
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = ''
AS $$
DECLARE
  v_msg RECORD;
BEGIN
  -- read_with_poll: 메시지가 올 때까지 최대 p_timeout_seconds 대기
  SELECT *
    INTO v_msg
    FROM pgmq.read_with_poll(
      p_queue,
      30,                  -- visibility timeout
      1,                   -- 1개만
      p_timeout_seconds,   -- long-poll 대기 시간
      200                  -- poll interval ms
    );

  IF v_msg IS NULL THEN
    RETURN NULL;
  END IF;

  -- pop 시맨틱스: 읽은 즉시 삭제
  PERFORM pgmq.delete(p_queue, v_msg.msg_id);

  RETURN v_msg.message;
END;
$$;

Client call

const { data, error } = await supabase.rpc('dequeue_task', {
  p_queue: 'tasks',
  p_timeout_seconds: 10
});

Safe pattern (at-least-once)

If you are worried about losing messages, the read → process → delete/archive pattern is recommended instead of pop().

-- 1. 읽기 (VT=60초 동안 다른 consumer에게 안 보임)
SELECT * FROM pgmq.read('my_queue', 60, 1);

-- 2. 처리 성공 후 삭제 또는 아카이브
SELECT pgmq.archive('my_queue', 123);

-- 처리 실패 시? → 60초 후 자동으로 다시 visible → 재처리

Summary

  • pgmq.pop() — ReadAndRemove already exists (at-most-once)
  • pgmq.read_with_poll() — supports blocking waits like Redis BRPOP
  • Wrap it in an RPC — combining read_with_poll + delete lets you implement a pop RPC with a timeout
  • Can also be called directly from the Supabase client via the pgmq_public.* schema

See also

Favorite site

author Zer0 sitemap