diff --git a/CHANGELOG.md b/CHANGELOG.md index 263d126d..9abc27b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Fixed periodic jobs advancing their durable next run time when job insertion fails. [PR #1359](https://github.com/riverqueue/river/pull/1359). +- Improved SQLite queue count performance on large job tables by limiting counts to available and running jobs so the existing state and queue index can be used. [PR #1360](https://github.com/riverqueue/river/pull/1360). ## [0.44.1] - 2026-08-21 diff --git a/riverdriver/riverdrivertest/job_read.go b/riverdriver/riverdrivertest/job_read.go index fd6ad6d1..f8fe4418 100644 --- a/riverdriver/riverdrivertest/job_read.go +++ b/riverdriver/riverdrivertest/job_read.go @@ -116,6 +116,34 @@ func exerciseJobRead[TTx any](ctx context.Context, t *testing.T, executorWithTx require.Equal(t, int64(1), countsByQueue[1].CountRunning) }) + t.Run("IgnoresJobsInOtherStates", func(t *testing.T) { + t.Parallel() + + exec, _ := setup(ctx, t) + + for _, state := range []rivertype.JobState{ + rivertype.JobStateCancelled, + rivertype.JobStateCompleted, + rivertype.JobStateDiscarded, + rivertype.JobStatePending, + rivertype.JobStateRetryable, + rivertype.JobStateScheduled, + } { + _ = testfactory.Job(ctx, t, exec, &testfactory.JobOpts{Queue: new("queue1"), State: new(state)}) + } + + countsByQueue, err := exec.JobCountByQueueAndState(ctx, &riverdriver.JobCountByQueueAndStateParams{ + QueueNames: []string{"queue1"}, + Schema: "", + }) + require.NoError(t, err) + + require.Len(t, countsByQueue, 1) + require.Equal(t, "queue1", countsByQueue[0].Queue) + require.Equal(t, int64(0), countsByQueue[0].CountAvailable) + require.Equal(t, int64(0), countsByQueue[0].CountRunning) + }) + t.Run("IncludesRequestedQueuesThatHaveNoJobs", func(t *testing.T) { t.Parallel() diff --git a/riverdriver/riversqlite/internal/dbsqlc/river_job.sql b/riverdriver/riversqlite/internal/dbsqlc/river_job.sql index 6f8bd9a4..dd284e7e 100644 --- a/riverdriver/riversqlite/internal/dbsqlc/river_job.sql +++ b/riverdriver/riversqlite/internal/dbsqlc/river_job.sql @@ -63,6 +63,7 @@ WITH queue_stats AS ( COUNT(CASE WHEN river_job.state = 'running' THEN 1 END) AS count_running FROM /* TEMPLATE: schema */river_job WHERE river_job.queue IN (sqlc.slice('queue_names')) + AND river_job.state IN ('available', 'running') GROUP BY river_job.queue ) diff --git a/riverdriver/riversqlite/internal/dbsqlc/river_job.sql.go b/riverdriver/riversqlite/internal/dbsqlc/river_job.sql.go index 61ef76b9..68701243 100644 --- a/riverdriver/riversqlite/internal/dbsqlc/river_job.sql.go +++ b/riverdriver/riversqlite/internal/dbsqlc/river_job.sql.go @@ -109,6 +109,7 @@ WITH queue_stats AS ( COUNT(CASE WHEN river_job.state = 'running' THEN 1 END) AS count_running FROM /* TEMPLATE: schema */river_job WHERE river_job.queue IN (/*SLICE:queue_names*/?) + AND river_job.state IN ('available', 'running') GROUP BY river_job.queue )