Render Tutorials
Deploy an AI agent three ways on Render

Trace the ack contract

⏱ 6 min

The worker deploy unlocked two things in-process review couldn’t do: scale the agent independently, and keep work alive when the web tier restarts. Before you prove that in the Dashboard, read the one part that makes the worker pattern real: message acknowledgement.

1. Trace the ack semantics

Open the queue helper and find the entry-processing function. In the current workshop repo, the function is implemented so you can inspect the contract directly:

  • Queue helper: packages/queue-agents/src/kv.ts
  • Function: processEntry
  • Queue helper: packages/queue_agents/src/queue_agents/kv.py
  • Function: process_entry
  • Parse the stream entry into a job.
  • Run the handler.
  • On success, call XACK so the consumer group does not redeliver the message.
  • On failure, log and return without acking. The message stays pending and can be retried.
  1. Open the queue helper In your fork, open the queue helper for your track.
  2. Find the entry processor Read the try block: it parses the entry, runs the handler, then calls xack only after success.
  3. Check the failure path Read the error path: it logs and returns without acknowledging the stream entry, so the message stays pending for retry.

This is the queue ownership the rest of the workshop contrasts with Workflows. Acknowledging too early can lose work. Letting errors escape can kill the consumer loop. Forgetting to ack success means Redis keeps the job pending forever.

2. Verify the queue behavior

Start Redis or Valkey locally if it is not already running:

Terminal
redis-server
Terminal
docker run --rm -p 6379:6379 redis

In another terminal, run the focused worker test:

Terminal
VALKEY_URL=redis://127.0.0.1:6379 npm run test:worker
Terminal
VALKEY_URL=redis://127.0.0.1:6379 uv run pytest tests/integration/test_queue_kv.py

All three focused tests should pass: the success ack, the failed-handler pending state, and a stale pending entry reclaimed for another attempt. If the run reports 0 tests or skipped, the suite never reached a live Redis or Valkey, see Troubleshooting. The repo ships this implementation so you can inspect the contract, verify it locally, and connect that contract to the deployed behavior.

Troubleshooting

Find the symptom that matches what you’re seeing, then apply the fix.

The test reports green but ran zero tests. Both worker suites skip themselves when VALKEY_URL is unset, and the exit code is still 0. Confirm the run actually executed: you want 3 passing tests (acks on success, leaves un-acked on failure, redelivers a pending entry). A green run with 0 tests or skipped means VALKEY_URL didn’t reach the process. Always pass it inline, as shown in the command.


command not found: redis-server or Cannot connect to the Docker daemon. Install Redis (brew install redis && redis-server) or run docker run --rm -p 6379:6379 redis with Docker Desktop running. Verify with redis-cli ping returning PONG before running the test, and keep that terminal open.


Address already in use on port 6379. Something is already on the default port. You don’t need a second instance: confirm it with redis-cli ping and point the test at it. To clear a stray one: docker rm -f <id> or brew services stop redis.


The test passes but connection-refused in real use. The test only skips on a missing env var, not a missing server. ECONNREFUSED 127.0.0.1:6379 means no Redis/Valkey is actually running on that port.


command not found: pytest. pytest lives in the workspace .venv, not on your PATH. Keep the uv run prefix shown in the command above; bare pytest won’t resolve.

What you learned

  • Traced the entry processor: successful jobs are acked and failed jobs stay pending for retry
  • Ran the focused worker test against local Redis or Valkey to prove the ack boundary