Skip to content

Job list: time sorting and created-at filtering - #720

Open
mheers wants to merge 4 commits into
riverqueue:masterfrom
mheers:job-sort-time
Open

mheers wants to merge 4 commits into
riverqueue:masterfrom
mheers:job-sort-time

Conversation

@mheers

@mheers mheers commented Oct 28, 2026

Copy link
Copy Markdown

Adds a newest/oldest toggle and a created-at range filter to the Jobs pane.

  • GET /api/jobs accepts sort_order=asc|desc, created_after, and created_before (RFC 3339). An explicit sort order overrides the existing per-state defaults; when sort_order is omitted, the current defaults stay as they are.
  • The range is applied with JobListParams.Where("created_at >= @created_after", ...), which is applied by both the PostgreSQL and SQLite drivers.
  • The UI retains the params across navigation and includes them in the jobs query key, so cancel, delete, and retry refresh the active result set.
  • Adds backend and frontend coverage.

Screenshots

Newest-first sort:

Job list sorted newest first

Created-at range filter:

Job list created-at range filter

Open question: Where vs. a River core API

Tag filtering landed upstream through a first-class JobListParams API (riverqueue/river#1339), while this uses JobListParams.Where with named args. Where is applied by both drivers, and River has no existing created-at filter or ordering field.

Would you prefer that I add CreatedAfter / CreatedBefore (and possibly a created-at ordering field) to riverqueue/river first and pin to it, or is the Where approach acceptable here?

Notes

  • Sorting uses River's JobListOrderByTime, so it orders by the state-specific timestamp (scheduled_at, attempted_at, or finalized_at depending on state). The created-at range filter is independent of that.
  • I can move the controls into the existing JobSearch filter grammar (for example created:) instead if that fits the direction better.
  • Opening as a draft while we settle the API question. I'll sign the CLA before marking it ready for review.

Add sort_order=asc|desc and created_after/created_before (RFC 3339)
query parameters to GET /api/jobs. Explicit values override the
existing per-state defaults while the old defaults are preserved when
no sort order is given. The created-at range is applied through
JobListParams.Where, which works across PostgreSQL and SQLite.

Expose both in the UI with a newest/oldest toggle and a created-at
range popover in the job list header. The search params are retained
across navigation and included in the jobs query key so cancel, delete,
and retry mutations refresh the active result set.

Adds backend coverage for sort order and created-at filtering.
Cover the sort order and created-at range filters at the component and
route layers: the JobList controls report changes, the search schema
accepts asc/desc and timestamps, the active query key carries all three
filters, and the route callbacks round-trip values into search.
The Time Range label wrapped to two lines when the job search input took
up the remaining header width.
@brandur

brandur commented Oct 29, 2026

Copy link
Copy Markdown
Collaborator

Thanks @mheers! Blake wants to take a look at a couple things related to index performance before bringing this in, but in the meantime, would you mind signing the CLA? https://github.com/riverqueue/rivercla

@mheers
mheers marked this pull request as ready for review October 29, 2026 10:35

@bgentry bgentry left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for the PR! From an initial review pass, the main issue is that filtering by created_at makes this fall back to a sequential scan because there's no relevant index to be used. This doesn't matter much at small scale, but in large prod deployments it could cause some real load issues if somebody leaves open this view with frequent auto refresh enabled.

I'm also inclined to put the filters directly in the main river repo. Originally we were only putting filters/sorts/etc in there that we knew could be done efficiently, but as usage grew we accepted that it was still better to include more flexible options even if they weren't necessarily going to be efficient. That way people can still build custom tooling that leverages the query APIs for infrequent use cases where the perf hit doesn't matter. So I think we can still move these filters over there in a standalone PR.

Meanwhile, @brandur and I should probably discuss what we want to do about UI use cases that we can't serve efficiently without adding more indexes (which we generally don't want to force on everyone due to impact on throughput, table sizes, etc).

@brandur

brandur commented Oct 29, 2026

Copy link
Copy Markdown
Collaborator

Thanks for the PR! From an initial review pass, the main issue is that filtering by created_at makes this fall back to a sequential scan because there's no relevant index to be used. This doesn't matter much at small scale, but in large prod deployments it could cause some real load issues if somebody leaves open this view with frequent auto refresh enabled.

Just to clarify Blake: you consider this a blocker?

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.

3 participants