Concurrency & Lock Management

How Paperclip prevents overlapping runs for the same agent using PostgreSQL advisory locks and row-level locking. What happens when a lock can't be acquired and the fallback behavior.

Concurrency: Multiple Agents Per Tick

What you'll learn

How Paperclip runs multiple agents in parallel within a single tick, configure concurrency limits, and understand the performance trade-offs.

Default Concurrency

By default, Paperclip invokes all active agents in parallel within one tick using Promise.allSettled. With five active agents, all five adapter calls start simultaneously.

Tick T=0:00
  Agent A invoke ──────────────────► complete T=0:28
  Agent B invoke ──────────────────► complete T=0:31
  Agent C invoke ──────────────────► complete T=0:15
  Agent D invoke ──────────────────► complete T=0:42
  Agent E invoke ──────────────────► complete T=0:35
Tick T=0:42 (overall completion time = slowest agent)

The tick's completion time equals the slowest agent, not the sum of all durations.

Maximum Concurrent Agents

Control how many agents run in parallel:

Company Settings → Heartbeat → Max concurrent agents (default: 10).

Or in the heartbeat config:

// Limit to 3 parallel adapters
const semaphore = new Semaphore(maxConcurrentAgents ?? 10);

await Promise.allSettled(
  agents.map(agent =>
    semaphore.acquire().then(release =>
      dispatchOne(agent, ...).finally(release)
    )
  )
);

When to Limit Concurrency

Situation Recommended limit
Free-tier API keys (strict rate limits) 1–2
Production with standard API keys 5–10 (default)
Self-hosted models (Ollama on a single GPU) 1
Multiple cloud providers, generous rate limits 20+

Rate Limit Handling

When the LLM API returns 429 (rate limit), the adapter throws and the issue is marked as failed + retried next tick. Set a lower concurrency limit to stay within your API quota.

For claude-code adapter specifically: Anthropic rate limits are per-API-key and per-model. Using different models (claude-haiku vs claude-opus) for different agents can double your effective throughput.

Per-Agent Parallelism

By default each agent claims one issue per tick. To let an agent claim and process multiple issues in one tick:

{
  "adapter_config": {
    "max_issues_per_tick": 3
  }
}

This is useful for a "light" agent doing quick classification tasks. Do not use it for expensive LLM agents unless you're confident about budget.

Monitoring Concurrency

-- See max concurrent adapters in a given run
SELECT
  id,
  agents_invoked,
  issues_claimed,
  ROUND(
    EXTRACT(epoch FROM (finished_at - started_at)) / NULLIF(agents_invoked, 0)
  ) AS avg_seconds_per_agent,
  EXTRACT(epoch FROM (finished_at - started_at)) AS total_seconds
FROM heartbeat_runs
WHERE company_id = 'your-company-id'
ORDER BY started_at DESC
LIMIT 5;

Checkpoint ✓

Sign in for progress tracking & Q&A →