Skip to content

fix(queue): raise River JobTimeout for large-document ingest#50

Merged
hallelx2 merged 2 commits into
mainfrom
halleluyaholudele/engine-ingest-job-timeout
Jul 3, 2026
Merged

fix(queue): raise River JobTimeout for large-document ingest#50
hallelx2 merged 2 commits into
mainfrom
halleluyaholudele/engine-ingest-job-timeout

Conversation

@hallelx2

@hallelx2 hallelx2 commented Jul 3, 2026

Copy link
Copy Markdown
Owner

River's client JobTimeout defaults to 1 minute, cancelling the ingest job mid-flight on large documents. Summarize + HyDE + ToC each issue one LLM call per section, so a 100+ section document needs far longer than 60s — it was failing with repeated context deadline exceeded and retrying until discarded.

Adds a configurable RiverConfig.JobTimeout (generous 30-minute default) passed to the River client, so full-document summarization runs to completion.

Verified live: a 91-section document now summarizes in ~98s and reaches ready (previously died at the 60s mark).

Summary by CodeRabbit

  • New Features
    • Added support for configuring how long individual queue jobs can run before they’re automatically canceled.
    • Set a sensible default timeout for jobs when no custom value is provided, helping long-running tasks complete more reliably.

River's client JobTimeout defaults to 1 minute, which cancels the ingest
job mid-flight on large documents: summarize + HyDE + ToC issue one LLM
call per section, and a 100+ section document needs far longer than 60s
(it was failing with repeated 'context deadline exceeded' and retrying).

Add a configurable RiverConfig.JobTimeout with a generous 30-minute
default and pass it to the River client, so full-document summarization
runs to completion.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @hallelx2, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@hallelx2, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 52 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7b0b72bd-d4bc-43df-b7cf-35aae8c10859

📥 Commits

Reviewing files that changed from the base of the PR and between 3a2cbb9 and b5eed6d.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (1)
  • go.mod
📝 Walkthrough

Walkthrough

Adds a configurable JobTimeout field to RiverConfig in pkg/queue/river.go. NewRiver defaults this to 30 minutes when unset or non-positive, and ensureClient passes the configured timeout into river.Config to bound job execution duration.

Changes

River job timeout configuration

Layer / File(s) Summary
Add and wire JobTimeout config
pkg/queue/river.go
Adds time import, adds JobTimeout time.Duration field to RiverConfig, sets a default of 30 minutes in NewRiver when unset, and passes the configured value into river.Config in ensureClient.

Estimated code review effort: 1 (Trivial) | ~5 minutes

Related PRs: None identified.

Suggested labels: enhancement, queue

Suggested reviewers: None identified.

🐰 A timer ticks where jobs once ran unbound,
Thirty minutes now the edge they've found,
A duration whispered into River's core,
Configs guard the queue forevermore.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: increasing River job timeout to prevent large-document ingest jobs from timing out.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch halleluyaholudele/engine-ingest-job-timeout

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
pkg/queue/river.go (2)

26-32: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Doc comment doesn't mention -1 disables the timeout.

The comment says "Zero applies a generous default" but doesn't note that River also treats negative values (specifically -1) as "no timeout" — which, given the <= 0 check in NewRiver (Line 118), is currently indistinguishable from zero anyway. Worth clarifying once/if the check above is fixed to preserve that distinction.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/queue/river.go` around lines 26 - 32, Update the JobTimeout doc comment
to explicitly document the negative-value behavior alongside the zero default.
The comment currently only says zero uses a generous default, but NewRiver also
treats JobTimeout <= 0 as the default path, so clarify whether -1 is intended to
mean “no timeout” and align the comment with the actual handling in NewRiver and
JobTimeout. Keep the wording consistent with the existing timeout behavior and
note the distinction if the <= 0 check is changed later.

26-32: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider a unit test for the default-timeout branch.

NewRiver's zero/negative-to-30-minute defaulting logic (and the pass-through when a positive value is set) isn't covered by a dedicated test — TestRiverIntegration in pkg/queue/river_test.go never sets JobTimeout and doesn't assert on it. A small unit test asserting cfg.JobTimeout ends up as 30 minutes when unset, and is preserved when explicitly set, would guard this behavior against regression.

Also applies to: 118-123

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/queue/river.go` around lines 26 - 32, Add a focused unit test for
NewRiver in pkg/queue/river_test.go that covers the JobTimeout defaulting
behavior. Verify that when the config’s JobTimeout is zero or negative, NewRiver
sets the effective timeout to 30 minutes, and that a positive JobTimeout is
passed through unchanged. Use the NewRiver constructor and the JobTimeout field
on the River config to keep the test aligned with the existing defaulting logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pkg/queue/river.go`:
- Around line 118-123: In queue initialization, the JobTimeout fallback
currently treats River’s explicit no-timeout sentinel the same as an unset
value; update the logic in river.go so only cfg.JobTimeout == 0 gets defaulted
to 30 minutes. Keep the existing default assignment behavior for unset timeouts,
but preserve an explicit -1 by leaving it unchanged in the code path around
cfg.JobTimeout.

---

Nitpick comments:
In `@pkg/queue/river.go`:
- Around line 26-32: Update the JobTimeout doc comment to explicitly document
the negative-value behavior alongside the zero default. The comment currently
only says zero uses a generous default, but NewRiver also treats JobTimeout <= 0
as the default path, so clarify whether -1 is intended to mean “no timeout” and
align the comment with the actual handling in NewRiver and JobTimeout. Keep the
wording consistent with the existing timeout behavior and note the distinction
if the <= 0 check is changed later.
- Around line 26-32: Add a focused unit test for NewRiver in
pkg/queue/river_test.go that covers the JobTimeout defaulting behavior. Verify
that when the config’s JobTimeout is zero or negative, NewRiver sets the
effective timeout to 30 minutes, and that a positive JobTimeout is passed
through unchanged. Use the NewRiver constructor and the JobTimeout field on the
River config to keep the test aligned with the existing defaulting logic.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3228e79b-1914-408d-920c-e3d7caf2b4ba

📥 Commits

Reviewing files that changed from the base of the PR and between 42c34fc and 3a2cbb9.

📒 Files selected for processing (1)
  • pkg/queue/river.go

Comment thread pkg/queue/river.go
Comment on lines +118 to +123
if cfg.JobTimeout <= 0 {
// Large documents (100+ sections) need well over River's 1-minute
// default to finish every per-section LLM summary. 30 minutes is a
// safe ceiling that still guards against a genuinely wedged job.
cfg.JobTimeout = 30 * time.Minute
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '## river.go excerpt\n'
sed -n '1,180p' pkg/queue/river.go

printf '\n## river dependency/version\n'
rg -n 'river|github.com/riverqueue/river' go.mod go.sum . -g '!**/vendor/**' || true

Repository: hallelx2/vectorless-engine

Length of output: 23663


🌐 Web query:

River JobTimeout -1 disable timeout documented

💡 Result:

In the River job queue library for Go, setting JobTimeout to -1 is the documented way to disable job timeouts [1][2]. When JobTimeout is set to -1: - The job's context will not be cancelled by the River client due to a timeout [2]. - The job will continue to run indefinitely unless the Client itself is shut down (e.g., via a graceful shutdown) [2]. This configuration is available at both the client-wide level (in the river.Config struct) and the individual worker level (by returning -1 in the worker's Timeout method) [1]. While this functionality is supported, it is generally recommended to use timeouts to ensure jobs remain cancellable and to avoid issues with stuck jobs [1][3].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '## search for JobTimeout usage/docs in repo\n'
rg -n 'JobTimeout|job timeout|disable timeout|timeout sentinel|-1' . -g '!**/vendor/**' || true

Repository: hallelx2/vectorless-engine

Length of output: 16123


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '## River config struct and validation\n'
sed -n '320,430p' pkg/config/config.go
printf '\n---\n'
sed -n '1238,1260p' pkg/config/config.go

printf '\n## docs and examples mentioning river timeout\n'
rg -n 'job[_-]?timeout|timeout' config.example.yaml config.server.example.yaml README.md docs pkg/config -g '!**/vendor/**' || true

Repository: hallelx2/vectorless-engine

Length of output: 6938


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '## river package config usage around JobTimeout\n'
sed -n '220,250p' pkg/queue/river.go

printf '\n## any tests for RiverConfig or timeout behavior\n'
rg -n 'JobTimeout|NewRiver\\(|RiverConfig' pkg/queue pkg/config -g '*_test.go' || true

Repository: hallelx2/vectorless-engine

Length of output: 1254


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '## search River docs for -1 sentinel and default semantics from source files in repo comments\n'
sed -n '1,120p' docs/ENGINE.md | sed -n '295,320p' || true

Repository: hallelx2/vectorless-engine

Length of output: 258


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '## NewRiver call sites\n'
rg -n 'NewRiver\\(' . -g '!**/vendor/**' || true

printf '\n## River config field definitions and defaults\n'
sed -n '680,750p' pkg/config/config.go

Repository: hallelx2/vectorless-engine

Length of output: 2145


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '## NewRiver call sites and River config translation\n'
rg -n 'NewRiver\\(|RiverConfig\\{' cmd internal pkg -g '!**/vendor/**' || true

printf '\n## relevant config River block\n'
sed -n '330,380p' pkg/config/config.go

Repository: hallelx2/vectorless-engine

Length of output: 1881


Preserve River's -1 timeout sentinel. cfg.JobTimeout <= 0 also rewrites an explicit no-timeout setting to 30 minutes; switch this to == 0 so only the unset zero value gets the default.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/queue/river.go` around lines 118 - 123, In queue initialization, the
JobTimeout fallback currently treats River’s explicit no-timeout sentinel the
same as an unset value; update the logic in river.go so only cfg.JobTimeout == 0
gets defaulted to 30 minutes. Keep the existing default assignment behavior for
unset timeouts, but preserve an explicit -1 by leaving it unchanged in the code
path around cfg.JobTimeout.

Resolves the govulncheck finding: a webp alpha-channel panic in
golang.org/x/image reachable via the PDF parser's image support.
@hallelx2 hallelx2 merged commit e637d13 into main Jul 3, 2026
8 checks passed
@hallelx2 hallelx2 deleted the halleluyaholudele/engine-ingest-job-timeout branch July 3, 2026 01:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant