Runnel 2.8: replay any workflow from a failed step.Read the release

Skip to content
Runnel

2.2.0

Concurrency keys: limit runs per customer, not per job

A concurrency limit can now apply per key, so one busy customer can no longer hold up everyone else's runs.

  • New
  • Fixed

Concurrency keys

Until now a concurrency limit applied to a whole job: limit: 20 meant 20 runs at once, across all your customers. When one customer imported 50,000 contacts, everyone else’s imports waited behind them.

A limit can now take a key. Runnel keeps a separate count for each key value, so the limit below allows 2 runs at a time per account and any number of accounts at once.

export const importContacts = runnel.job({
  name: 'import-contacts',
  concurrency: { limit: 2, key: (p) => p.accountId },
  run: async (payload: { accountId: string; fileId: string }, ctx) => { /* … */ },
});

Python takes concurrency=Concurrency(limit=2, key=lambda p: p["account_id"]), and Go runnel.WithConcurrencyKey(2, fn). Runs waiting for a slot show as queued with the reason “Waiting for concurrency key acc_42” in the dashboard.

Keys are free on every plan. A job can have up to 100,000 active keys.

Fixes

  • A run cancelled while its attempt was starting could be marked running for up to a minute.
  • runnel dev now reloads when runnel.config.ts changes, not only when job files do.
  • Fixed a memory leak in the TypeScript worker when a job logged objects with circular references.