Skip to content

emilioSp/justusepg

Repository files navigation

Just Use Postgres

Why add another dependency when PostgreSQL can be your queue?

This project demonstrates how ridiculously simple it is to build a production-ready work queue using just PostgreSQL.

No Redis, no RabbitMQ, no SQS—just Postgres.

Descrizione

Why did I write this?

Because simplicity is underrated.

You don't need to introduce more moving parts into your architecture when your database can handle it all.

The Magic: One SQL Query

SELECT * FROM tasks 
WHERE status = 'pending' 
ORDER BY created_at 
FOR UPDATE SKIP LOCKED 
LIMIT 1

That's it. FOR UPDATE SKIP LOCKED ensures multiple workers can safely process tasks concurrently without stepping on each other's toes.

It's basic, reliable, and leverages PostgreSQL's robust ACID guarantees.

It's already in your stack, battle-tested, you just have to use it.

Software components

1. DB Schema

CREATE TABLE tasks (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  payload JSONB NOT NULL,
  status TEXT NOT NULL DEFAULT 'pending',
  created_at TIMESTAMP DEFAULT NOW(),
  picked_at TIMESTAMP,
  executed_at TIMESTAMP
);

2. Producer (Add Tasks)

Continuously adds tasks to the queue.

3. Consumers (Process Tasks)

Workers that process tasks in parallel—all safely thanks to PostgreSQL's locking mechanism.

Quick Start

# Start PostgreSQL
docker compose up -d

# Run migrations
npm run migrate

# Let's the party begin
npm run start

Watch as multiple workers process different tasks simultaneously without any race conditions. Each producer is able to fetch one task at a time, process it, and mark it as done. A task is never processed more than once. A task is never assigned to more than one worker at a time.

What This Proves

You don't always need specialized queue infrastructure. PostgreSQL is:

  • Simple - Already in your stack
  • Reliable - Battle-tested ACID guarantees
  • Performant - Handles thousands of jobs/second
  • Cost-effective - No additional services to pay for

and hey, you don't need to consume a "message" to see its payload! :D (just query the tasks table directly!)

When NOT to Use This

  • You need millions of messages per second
  • You need pub/sub patterns across many many...many services
  • You need delayed/scheduled jobs at scale

But for 80% of use cases? Just use Postgres.

Tech Stack

  • PostgreSQL (with SKIP LOCKED)
  • Node.js + TypeScript
  • Knex.js (query builder)
  • Docker Compose

The best queue is the one you don't have to add. 🚀

About

The best queue is the one you don't have to add

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published