In-Memory Adapter

August 20, 2026 ยท View on GitHub

The in-memory adapter is a drop-in replacement for the PostgreSQL backend, so PgQueuer can run entirely without a database connection. Located in pgqueuer.adapters.inmemory, it provides InMemoryDriver and InMemoryQueries classes that satisfy the same RepositoryPort protocol as the production PostgreSQL-backed implementation.

QueueManager and SchedulerManager work unchanged against the in-memory adapter. You don't need to rewrite your job handlers or business logic.

The simplest way to use it is via the factory method:

from pgqueuer import PgQueuer

pq = PgQueuer.in_memory()

Or import the classes directly:

from pgqueuer import InMemoryDriver, InMemoryQueries

Quick start

import asyncio
from pgqueuer import PgQueuer
from pgqueuer.models import Job
from pgqueuer.types import QueueExecutionMode

async def main():
    pq = PgQueuer.in_memory()

    @pq.entrypoint("say_hello")
    async def say_hello(job: Job) -> None:
        print(f"Processing job {job.id}: {job.payload}")

    job_ids = await pq.qm.queries.enqueue(
        ["say_hello"] * 5,
        [b"job 1", b"job 2", b"job 3", b"job 4", b"job 5"],
        [0] * 5,
    )
    print(f"Enqueued {len(job_ids)} jobs")

    await pq.qm.run(
        batch_size=10,
        mode=QueueExecutionMode.drain,
        max_concurrent_tasks=100,
    )
    print("All jobs processed!")

asyncio.run(main())

When to use the in-memory adapter

Reach for it when the queue only has to live as long as one process:

  • Unit and integration tests, with no PostgreSQL instance or Docker container to start
  • CI pipelines on resource-constrained runners (GitHub Actions, lightweight containers)
  • Local development and prototyping, before you set up infrastructure
  • Short-lived batch containers that process a fixed job set and exit (ETL, one-time cleanup)

Avoid it when any of the following matter:

  • Durability. A restart loses every queued and in-flight job, so nothing survives a crash.
  • More than one process or machine. Jobs are invisible outside the process that enqueued them.
  • Transactions. There is no rollback and no atomic retry.
  • Post-mortem inspection. Once the process exits, the job history is gone.

Limitations reference

CapabilityPostgreSQL adapterIn-memory adapter
DurabilityPersists across restartsLost on process exit
Multi-process workersYes (row-level locking)No
Multi-node scalingYesNo
ACID transactionsYesNo (best-effort only)
LISTEN/NOTIFYReal Postgres channelsIn-process callbacks only
Schema operationsDDL executedNo-ops (always return True)
PerformanceBounded by network I/OCPU-bound; O(n) dequeue scan

Implementation notes

driver.fetch() and driver.execute()

Both methods raise NotImplementedError by design. InMemoryQueries operates directly on in-memory dictionaries and never executes SQL.

Event loop yielding

The dequeue() method includes an explicit await asyncio.sleep(0) to yield control to the event loop. This is critical: without it, QueueManager.fetch_jobs would starve signal handlers, timers, and concurrent jobs. The PostgreSQL adapter naturally yields during real I/O; the in-memory adapter must do so explicitly.

Schema management

  • install(), upgrade(): no-ops
  • uninstall(): clears all internal dictionaries, resetting queue state
  • Schema inspection methods always return True

Job state

The adapter maintains job state using plain Python dictionaries:

DictContents
_jobsActive job records
_logHistorical log entries (every status transition)
_statisticsAggregated statistics
_schedulesScheduled job definitions
_dedupe_indexDeduplication by key

All are cleared when uninstall() is called or when the process exits.

Notifications

InMemoryDriver emulates PostgreSQL LISTEN/NOTIFY via in-process callbacks:

await driver.notify(channel, payload)

Registered callbacks are invoked synchronously and immediately.