pgmem
Guides

Run the projection worker

Refresh claim-derived projections asynchronously.

When a session ends, its active claims are queued for derived lexical, vector, and graph projection refresh (Tier 4). PromotionWorker drains that work off the response path. It never changes claim authority, evidence, or lifecycle.

Prerequisites

  • An embedder for vector projection; lexical and graph projections work without one.
  • pool.max_size >= 2 for parallel application and worker activity.

Steps

Construct a worker

from pgmem import PromotionWorker

worker = PromotionWorker(mem)   # optionally lease_seconds=300

Run it as a background loop (production)

run polls the queue and processes jobs until you signal stop. Run it as a long-lived task alongside your agent.

import asyncio

stop = asyncio.Event()
task = asyncio.create_task(worker.run(poll_interval=5.0, stop=stop, use_listen=False))
# ... later, to shut down:
stop.set()
await task

Or drain on demand (tests, batch, scripts)

await worker.run_once()              # claim + process the next job (or None)
n = await worker.process_pending()   # drain up to max_jobs (default 1000)

Recover interrupted jobs on startup

A worker that crashed mid-job leaves a job leased. recover requeues only jobs whose lease has expired, so a fresh worker can pick them up without stealing live ones.

requeued = await worker.recover()

Expire elapsed retention deadlines

retention_until is enforced on reads immediately. Run ClaimRetentionWorker from the same scheduler or worker service to transition elapsed claims to expired, remove their projections, and queue affected narrative rebuilds:

from pgmem import ClaimRetentionWorker

retention = ClaimRetentionWorker(mem)
await retention.drain()  # or await retention.serve(poll_interval=60)

Verify

After session.end() and a worker pass, the active claim has a graph projection:

await session.end()
await worker.run_once()
count = await mem.driver.pool.fetchval(
    "SELECT count(*) FROM pgmem.memory_claim_graph_edges WHERE claim_uuid = $1",
    claim.uuid,
)
print(count)

Gotchas

Claim formation is policy-governed; projection is not a second authority gate. Workers derive indexes only from active claims and never turn a provisional claim into an accepted one.

  • Multiple workers are safe to run concurrently — jobs are claimed with FOR UPDATE SKIP LOCKED.
  • Embedding can be remote and must run off the response path.

On this page