Medium Impact

Use Advisory Locks for Application-Level Locking

Coordinate distributed operations without blocking tables

Why This Matters

Advisory locks are application-level locks that don't block table access. They're perfect for coordinating work across multiple application instances, preventing duplicate processing, or ensuring only one process handles a task at a time.

Unlike table locks, advisory locks:

Use case: Prevent duplicate email sends, coordinate scheduled jobs across multiple servers, or ensure only one worker processes a queue item.

The Problem

Without advisory locks, multiple application instances can process the same work simultaneously:

Incorrect - Race condition with multiple workers
-- Multiple workers can process the same job
BEGIN;
SELECT * FROM jobs WHERE status = 'pending' LIMIT 1;
-- Worker 1 and Worker 2 both see the same job
UPDATE jobs SET status = 'processing' WHERE id = 123;
-- Both workers process job 123 simultaneously!
COMMIT;

This causes duplicate processing, wasted resources, and potential data corruption.

The Solution

Use pg_try_advisory_lock() for non-blocking locks or pg_advisory_lock() for blocking locks:

Correct - Advisory lock prevents duplicates
-- Use advisory lock to coordinate job processing
BEGIN;

-- Try to acquire lock for job ID 123
SELECT pg_try_advisory_lock(123);
-- Returns true if lock acquired, false if already locked

SELECT * FROM jobs WHERE id = 123 AND status = 'pending';
-- Only one worker can process this job

UPDATE jobs SET status = 'processing' WHERE id = 123;
-- Process the job...

COMMIT;
-- Lock is automatically released on commit

For blocking locks that wait until available:

-- pg_advisory_lock waits until lock is available
SELECT pg_advisory_lock(123);
-- Blocks until lock is free, then acquires it

-- Always release explicitly in long-running transactions
SELECT pg_advisory_unlock(123);

Result: Only one worker processes each job, eliminating duplicate work and race conditions. Advisory locks are lightweight and don't block table access.

Best Practices

Warning: Advisory locks are per-database. If you need coordination across databases, use a dedicated coordination table or external locking service.

Check Your Project Automatically

Connect your Supabase project to detect locking issues and other concurrency problems.

Start Supabase Audit

Example: Preventing Duplicate Email Sends

Use advisory locks to ensure emails are only sent once, even with multiple workers:

-- Function to safely send email with advisory lock
CREATE OR REPLACE FUNCTION send_email_safely(email_id BIGINT)
RETURNS BOOLEAN AS $$
DECLARE
  lock_acquired BOOLEAN;
BEGIN
  -- Try to acquire lock using email ID
  SELECT pg_try_advisory_lock(email_id) INTO lock_acquired;
  
  IF NOT lock_acquired THEN
    -- Another process is already sending this email
    RETURN FALSE;
  END IF;
  
  -- Check if already sent
  IF (SELECT sent_at FROM emails WHERE id = email_id) IS NOT NULL THEN
    RETURN FALSE;
  END IF;
  
  -- Send email and mark as sent
  UPDATE emails SET sent_at = NOW() WHERE id = email_id;
  -- ... actual email sending logic ...
  
  RETURN TRUE;
END;
$$ LANGUAGE plpgsql;

Learn More