Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ GITTENSORY_REVIEW_DRAFT=false
# LITESTREAM_REGION=us-east-1

# --- Queue worker (#977/#1201) ---
# QUEUE_CONCURRENCY=1 # max concurrent job-processing loops per instance (default 1)
# QUEUE_CONCURRENCY=4 # max concurrent (I/O-bound) job loops per instance (default 4; 1 = serial)

# --- Caddy HTTPS terminator (#1203; requires --profile caddy) ---
# DOMAIN=gittensory.example.com # fully-qualified domain; Caddy auto-obtains a Let's Encrypt cert
Expand Down
3 changes: 2 additions & 1 deletion docs/self-hosting.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,8 @@ the **Orb-brokered** path:
(You set **no** `GITHUB_APP_*` secrets — the Orb holds the App key and mints tokens for you on demand.)

Runtime knobs: `PORT` (default 8787), `DATABASE_PATH` (default `/data/gittensory.sqlite`), `CRON_INTERVAL_MS`
(default 120000 ≈ the hosted every-2-minutes cron).
(default 120000 ≈ the hosted every-2-minutes cron), `QUEUE_CONCURRENCY` (default 4 — how many I/O-bound review
jobs process at once; raise it to drain bigger PR bursts faster, set `1` for strict serial processing).

**Secrets via files.** Any `FOO_FILE=/run/secrets/foo` is read into `FOO` at startup (Docker/Compose
secrets, multi-line keys) — an explicit `FOO` always wins.
Expand Down
6 changes: 4 additions & 2 deletions src/selfhost/pg-queue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,17 @@ export interface PgQueueOptions {
maxRetries?: number;
pollIntervalMs?: number;
backoffMs?: (attempt: number) => number;
/** Max concurrent `processOne()` loops. Defaults to QUEUE_CONCURRENCY env var or 1. */
/** Max concurrent `processOne()` loops. Defaults to QUEUE_CONCURRENCY env var or 4 — review jobs are I/O-bound
* (GitHub + AI awaits dominate), so overlapping a handful drains a PR burst far faster; FOR UPDATE SKIP LOCKED
* keeps claims race-free across the pool (and across replicas). Set QUEUE_CONCURRENCY=1 to force strict serial. */
concurrency?: number;
}

export function createPgQueue(pool: Pool, consume: (message: JobMessage) => Promise<void>, opts: PgQueueOptions = {}): PgDurableQueue {
const maxRetries = opts.maxRetries ?? 5;
const pollIntervalMs = opts.pollIntervalMs ?? 1000;
const backoff = opts.backoffMs ?? ((attempt: number) => Math.min(60_000, 1000 * 2 ** attempt));
const concurrency = opts.concurrency ?? Math.max(1, Number(process.env.QUEUE_CONCURRENCY ?? "1"));
const concurrency = opts.concurrency ?? Math.max(1, Number(process.env.QUEUE_CONCURRENCY ?? "4"));

let running = false;
let active = 0;
Expand Down
6 changes: 4 additions & 2 deletions src/selfhost/sqlite-queue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,17 @@ export interface SqliteQueueOptions {
maxRetries?: number;
pollIntervalMs?: number;
backoffMs?: (attempt: number) => number;
/** Max concurrent `processOne()` loops. Defaults to QUEUE_CONCURRENCY env var or 1. */
/** Max concurrent `processOne()` loops. Defaults to QUEUE_CONCURRENCY env var or 4 — review jobs are I/O-bound
* (GitHub + AI awaits dominate), so overlapping a handful drains a PR burst far faster while SQLite's WAL +
* busy_timeout absorb the short serialized write windows. Set QUEUE_CONCURRENCY=1 to force strict serial. */
concurrency?: number;
}

export function createSqliteQueue(driver: SqliteDriver, consume: (message: JobMessage) => Promise<void>, opts: SqliteQueueOptions = {}): DurableQueue {
const maxRetries = opts.maxRetries ?? 5;
const pollIntervalMs = opts.pollIntervalMs ?? 1000;
const backoff = opts.backoffMs ?? ((attempt: number) => Math.min(60_000, 1000 * 2 ** attempt));
const concurrency = opts.concurrency ?? Math.max(1, Number(process.env.QUEUE_CONCURRENCY ?? "1"));
const concurrency = opts.concurrency ?? Math.max(1, Number(process.env.QUEUE_CONCURRENCY ?? "4"));

driver.exec(DDL);
// Recover jobs a crashed previous run left mid-flight → make them claimable again.
Expand Down
Loading