DriversRecommendedOutdated drivers can make a good PC feel brokenScan driver issues before chasing fixes manually.Scan NowOctober DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsSlow PC?RecommendedPC slow today? Run a repair scan before it gets worseResolve common Windows issues and optimize system performance.Scan Now×
Skip to content
World desk5 min

Our Job Queue Is One Postgres Table—and Fairness Is Three ORDER BY Terms

A single PostgreSQL table can queue jobs durably. Claim work atomically with FOR UPDATE SKIP LOCKED, and order by priority, eligibility time, and a unique tie-breaker.
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Yes: PostgreSQL can serve as a durable job queue when your application already relies on it and jobs need to be created transactionally with business data. Have each worker claim a job by selecting, locking, and updating it in one statement. For priority-aware scheduling that favors older eligible work within each priority, use ORDER BY priority DESC, available_at ASC, id ASC.

When one PostgreSQL table is enough

A jobs table can hold both queued work and its progress through the system. That is especially useful when creating a job must commit or roll back with an application change—for example, recording an order and enqueueing the follow-up task in the same transaction. PostgreSQL then remains the source of truth for both the business record and the work it triggers.

As an Amazon Associate I earn from qualifying purchases.

A queue table is not a separate message broker: your application must define and operate the job lifecycle, including claims, retries, recovery, and cleanup. Whether that trade-off is right depends on transaction coupling, delivery semantics, throughput and latency needs, retry and dead-letter requirements, operational burden, and whether work must fan out across services.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

What the three ordering terms mean

Use an explicit ordering policy in the claim query:

ORDER BY priority DESC, available_at ASC, id ASC
  • priority DESC tries higher numeric priorities first.
  • available_at ASC favors the job that became eligible earlier among jobs with the same priority. It can also represent scheduled work: a future job is not claimable until its availability time arrives.
  • id ASC supplies a stable, unique tie-breaker when priority and availability time match. Make id a unique key, such as a primary key.

This is priority-aware preference, not a promise that every job runs in strict global timestamp order. Because priority sorts first, lower-priority work can wait behind higher-priority work. Under concurrency, a locked row can also be skipped, so a worker may claim a later eligible row instead.

If you want FIFO within each priority

You will also see ORDER BY priority DESC, created_at ASC, id ASC. That sorts by creation time within a priority, rather than by eligibility time. Prefer available_at when jobs may be delayed or scheduled; prefer created_at when the intended policy is arrival order. Bassam Ismail’s example describes priority ordering with FIFO within each priority: PostgreSQL as a job queue.

Claim jobs atomically with SKIP LOCKED

A separate read followed by an update is unsafe: two workers can read the same queued row before either has changed its status. PostgreSQL’s FOR UPDATE SKIP LOCKED lets concurrent consumers avoid waiting on rows another transaction has locked. Its result is intentionally inconsistent, so it is suitable for queue consumers—not general-purpose reporting. See the PostgreSQL 16 documentation.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use a locking subquery or CTE and update the selected row in the same statement:

WITH next_job AS (
  SELECT id
  FROM jobs
  WHERE status = 'queued'
    AND available_at <= now()
  ORDER BY priority DESC, available_at ASC, id ASC
  FOR UPDATE SKIP LOCKED
  LIMIT 1
)
UPDATE jobs AS j
SET status = 'running',
    locked_by = $1,
    locked_at = now()
FROM next_job
WHERE j.id = next_job.id
RETURNING j.*;

Here, $1 is the worker identifier. The statement selects one due job, locks it, changes its state and records ownership, then returns the claimed row. If no eligible unlocked row is available, it returns no row. Prisma’s walkthrough uses the same essential shape: lock an eligible row in the inner query and mark it running with the outer update in one atomic statement: You don’t need a job queue—Postgres already has SKIP LOCKED.

Keep the claim transaction short

Commit the claim before doing lengthy application work. The update records ownership; it does not require holding a database row lock throughout the job handler. Long-running work inside the claim transaction keeps locks open and can make the database a bottleneck, undermining the benefit of skipping locked rows.

Plan for retries and worker crashes

SKIP LOCKED prevents two workers from claiming the same row at the same moment. It does not guarantee exactly-once execution. If a worker crashes after its claim commits, the row can remain marked running even though the work did not finish. The practical contract is at-least-once execution: a recovery process needs to detect expired claims and return eligible jobs to the queue, using a lease, heartbeat, or reaper.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  • Make handlers idempotent so a retry does not duplicate an irreversible side effect.
  • Track attempts and set a retry budget so a persistently failing job does not loop forever.
  • Define how abandoned running jobs are recognized and recovered; locked_at and locked_by provide useful claim metadata, but the recovery policy belongs to the application.

For more on this execution caveat, see Bassam Ismail’s queue implementation discussion.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Index the claim workload and inspect its plan

Claims repeatedly search the queued, due working set and return rows in a defined order. An index aligned with the status filter and ordering can reduce scans through completed history. For example, this partial index is a starting point to test against the query and data distribution:

CREATE INDEX jobs_queued_claim_idx
ON jobs (priority DESC, available_at ASC, id ASC)
WHERE status = 'queued';

Do not assume the index guarantees a particular plan or speed: inspect the claim with EXPLAIN and measure it on your workload. Indexes also add write and maintenance cost as jobs are inserted and their statuses change. Bassam Ismail discusses the trade-off between an aligned index, a smaller working set, and the cost of maintaining broad indexes in the queue implementation example.

Percona Community reported a 1.68 ms median claim time at 16 workers in its particular 2026 workflow-engine benchmark. That is one benchmark result, not a general PostgreSQL capacity figure: schema, indexes, transaction duration, hardware, workload, and PostgreSQL version all affect performance. Percona Community’s benchmark

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Decide whether the database queue fits

  • Good fit: the application already uses PostgreSQL, and creating a job should commit atomically with application data.
  • Design it carefully: workers need concurrent claims, priority policy, retries, crash recovery, and operational monitoring. The table gives you persistence; your application supplies the lifecycle and delivery behavior.
  • Compare alternatives: if cross-service fan-out, retry or dead-letter workflows, delivery semantics, throughput, latency, or reduced operational burden are central, evaluate a dedicated queue against those requirements rather than choosing by habit.

There is no universal jobs-per-second promise for this pattern. Establish capacity with the actual schema, claim plan, worker count, transaction lengths, and lock or claim latency of your workload.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More from the Wire

  1. World desk4 min
    How to Spot an AI Voice Scam Before Sending MoneyDon’t rely on how a caller sounds. Pause, call back through a known number, and verify the emergency with another trusted person before sending money.
  2. Mountain View desk4 min
    Google’s SynthID Detector: How to Check AI-Generated Images, Video and AudioGoogle’s SynthID Detector looks for an embedded watermark in supported images, video and audio. Here is what its results do—and do not—show.
  3. Redmond desk20 min
    How to create a link to File or Folder in Windows 11Windows 11 gives you several ways to point to a file or folder without moving or duplicating it. You can create a desktop shortcut,…
Recommended PC Tool
Recommended PC Tool
Crashes, No Sound, or Screen Glitches?Free driver scan
PC Slower Than It Used to Be?Free scan - under a minute

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.