Migrate background jobs from DelayedJob to Solid Queue (#2656) - #2721
Open
stuzart wants to merge 51 commits into
Open
Migrate background jobs from DelayedJob to Solid Queue (#2656)#2721stuzart wants to merge 51 commits into
stuzart wants to merge 51 commits into
Conversation
…ows (#2656) Allow deploying with the adapter flipped to solid_queue and an empty queue as its own phase, and defer migrating leftover delayed_jobs rows to a separate, later phase. [skip ci]
Queue tables will live in the shared database (not separate) and must support MySQL, Postgres, and SQLite. Phase 5 migration decides how to handle locked (requeue) vs failed (delete) delayed_jobs rows. Remaining thread pool sizing question is resolved by the Phase 0 audit rather than needing separate input. [skip ci]
Audit found no issues with Rugged/git, HTTP clients, Sunspot, or image processing, but flagged two process-global mutable state points (User.current_user, $authorization_checks_disabled) used across several jobs. Mitigate by running 1 thread per queue for this migration rather than making them thread-local now. Also confirms Ruby/Rails versions already satisfy Solid Queue's requirements. [skip ci]
…2656) Job classes only declare a queue name via queue_as; process/thread topology lives entirely in config/queue.yml, so starting 1:1 with today's per-feature-flag queues carries no cost — consolidating later is a config-only change. Completes Phase 0. [skip ci]
Completes Phase 1 of SOLID_QUEUE_MIGRATION_PLAN.md: - Add solid_queue gem and a migration adding its tables to the shared primary database (not a separate queue database, per the Phase 0 decision) rather than using the generator's default multi-database setup. Reverted the generator's premature adapter flip in config/environments/production.rb - that's Phase 4, not now. - config/queue.yml: one worker per queue, gated by the same Seek::Config flags as Seek::Workers.active_queues, 1 thread per queue per the Phase 0 thread-safety findings. - config/recurring.yml: covers the job-enqueue entries from config/schedule.rb, with schedules matched against the actual output of `bundle exec whenever` for parity. Non-job entries stay on whenever/cron. - No ApplicationJob changes needed for retry/failure semantics - verified its rescue_from(Exception) already swallows exceptions inside perform_now itself, before any queue adapter sees them. [skip ci]
- Verified the solid_queue migration produces correct tables/schema version on MySQL, SQLite, and Postgres via scratch databases. - Ran the existing job unit tests (122) and a functional slice across git/data_files/samples controllers (390) unmodified, confirming no regressions from adding the gem - didn't force the whole test env onto queue_adapter: :solid_queue since that breaks ActiveJob::TestHelper's assert_enqueued_with, which requires the :test adapter specifically. - Ran a real SolidQueue::Worker against jobs on all 8 queues plus a Rugged/git-backed job (reading a real repo's HEAD), confirming successful execution across multiple real threads in one process - validates the Phase 0 thread-safety finding empirically, not just by inspection. Also validated every config/recurring.yml entry resolves to a real job class with a valid schedule. [skip ci]
Completes Phase 3 of SOLID_QUEUE_MIGRATION_PLAN.md. Unlike Phases 1-2, this replaces the DelayedJob worker infra outright rather than adding Solid Queue alongside it, so Phase 3 and Phase 4 (the adapter flip) must ship together in the same release - after this alone, nothing starts DelayedJob workers any more, but the adapter isn't flipped yet. - Delete lib/seek/workers.rb and lib/tasks/seek_workers.rake, and the now-unused daemons gem they depended on. - Add config/initializers/solid_queue.rb (supervisor pidfile) and script/run_solid_queue.sh, which runs bin/jobs in a loop and restarts it automatically unless signalled via its own pidfile - used by both Docker and non-Docker (script/update-from-git.sh, script/mini-update-from-git.sh) deployment paths. - Update docker/start_workers.sh, docker/entrypoint.sh, docker-compose.yml, script/check_worker_pids.sh, and the Dockerfile accordingly. - Update the admin "restart background job workers" button (AdminController#restart_job_workers, renamed) and the job queue admin pages to reflect Solid Queue's processes/jobs instead of DelayedJob's. - Switch ApplicationStatus#refresh (backs the /application_status endpoint some deployments poll) to a DB-backed process count instead of local pidfiles, which is also safe to read from any container in a split multi-container deployment. - Fix a Phase 1 oversight: config/recurring.yml was missing Solid Queue's own default finished-job cleanup task, needed since (unlike DelayedJob) it preserves finished jobs by default. - Update AGENTS.md/CLAUDE.md's background-jobs section now rather than deferring to Phase 6, since the commands it documented no longer exist as of this phase.
config.active_job.queue_adapter is now :solid_queue, so all newly enqueued jobs go to Solid Queue's tables. This ships together with Phase 3's infra rework, which already switched worker startup/monitoring/admin UI over to Solid Queue and stopped starting DelayedJob workers. delayed_job_active_record and the delayed_jobs table remain installed as a rollback safety net; migrating any rows still sitting there is handled separately in Phase 5. Verified via rails runner that ActiveJob::Base.queue_adapter resolves to SolidQueueAdapter in development, and that job unit tests (122) and admin/application-status functional tests (71) still pass unaffected.
Companion to SOLID_QUEUE_MIGRATION_PLAN.md - a walkthrough for exercising the new queue/worker setup locally: starting bin/jobs, enqueuing jobs on each queue, testing recurring jobs, using the admin UI, and the pidfile-based stop/restart semantics.
db/schema.rb's alphabetical table order put solid_queue_jobs before three tables with a foreign key referencing it (ready_executions, recurring_executions, scheduled_executions). MySQL's DROP TABLE CASCADE is a no-op, so reloading the schema onto a MySQL database that already has it loaded (as CI's rake db:setup job does) failed trying to drop solid_queue_jobs while those tables still held their FK. Move solid_queue_jobs's create_table block to after all six tables that reference it, so drop order is safe regardless of MySQL's lack of real cascading drops. Verified against real MySQL: reproduced the failure with two schema loads in a row, confirmed the fix, then ran rake db:setup end-to-end successfully.
ApplicationStatus#refresh was counting every SolidQueue::Process row (Supervisor + Dispatcher + Workers), not just workers, so the statistics/application_status endpoint and admin page overstated how many worker processes were running (e.g. 9 instead of 7 with one queue's feature disabled). Filter to kind: 'Worker' only. Also renamed the admin panel from "Solid Queue supervisor" to "Background job workers" (users administering SEEK don't need to know the gem name), and added a list of the currently active queues, sourced from each live worker's metadata.
config/schedule.rb still had six job-enqueue entries that were already migrated to config/recurring.yml in Phase 1 but never removed here, so every deploy running `whenever --update-crontab` would double-enqueue them: once via cron, once via Solid Queue's scheduler. Removed them, leaving only the rake tasks and Docker-only shell command that don't map onto recurring.yml. Also moved three more entries (Galaxy::ToolMap.instance.refresh, ApplicationStatus.instance.refresh, Seek::BioSchema::DataDump.generate_dumps) into recurring.yml as command: entries - SolidQueue::RecurringJob#perform just evals the command string, so it isn't limited to ActiveJob calls as Phase 1 assumed. While doing this, found that command:-only recurring.yml entries (including the already-shipped clear_finished_jobs and queue_timed_jobs) enqueue onto solid_queue_recurring, a queue config/queue.yml had no worker for - so they had never actually run. Added a worker for it. Verified live against a running supervisor both before (job stuck) and after (job completes) the fix. Added test/integration/recurring_test.rb covering config correctness, cron validity, queue/worker coverage (fails without the queue.yml fix), and end-to-end execution. Rewrote test/integration/schedule_test.rb for the trimmed schedule.rb, asserting no runner jobs remain.
config/queue.yml's workers inherited Solid Queue's gem-level default polling_interval (0.1s), which assumes the stock topology of one worker polling all queues via a wildcard. Our one-worker-per-queue topology (Phase 0's thread-safety mitigation) multiplies that into up to 9 separate continuous polling loops - up to ~90 poll queries/second against the DB even when everything is idle. Raised to 1s for all queues except AUTH_LOOKUP (0.5s, the highest- frequency, most latency-sensitive queue). Trades a small amount of job pickup latency for a large reduction in idle polling load; doesn't change the topology or thread-safety mitigation itself.
Comments added during the migration pointed at SOLID_QUEUE_MIGRATION_PLAN.md, which will be removed once the migration is complete. Rewrote them to carry their own context instead of relying on a doc that won't exist long-term.
regular_maintenance and auth_lookup_maintenance had their schedules hardcoded to `0 */4` / `0 */8`, dropping the regular_job_offset minute stagger that the old whenever schedule applied. whenever kept the */N hour field but set the minute from the `at:` offset for hour-frequency jobs, so on any instance with a non-zero offset these two jobs now wrongly fired at minute :00 instead of the staggered minute. Add an offset_minute helper and use it for both, and correct the comment/tests that claimed hour-frequency jobs don't get the offset.
SolidQueue::FailedExecution#error is a JSON hash (exception_class, message, backtrace), so rendering it directly dumped a raw Ruby hash inspect with the whole backtrace inlined - a regression from delayed_job's formatted last_error. Format it back into a readable "Class: message" + backtrace string using the model's accessors. Also eager-load :failed_execution so the per-row failure lookup no longer issues one query per pending job.
# Conflicts: # config/schedule.rb # test/integration/schedule_test.rb
CacheOverflowCleanupJob arrived from main as a whenever runner entry. It runs daily, so whenever applied both the hour and minute of the offset - use offset_cron(4) to match. Also restores schedule_test.rb, which the merge from main reverted to a version testing runner jobs that have since moved to recurring.yml. Its assertion that schedule.rb has no runner jobs is what guards against exactly this.
RUN_PERIOD and LifeMonitorStatusJob::PERIOD existed to feed `every X` in config/schedule.rb. That reader is gone - the schedules now live in config/recurring.yml, asserted by RecurringTest - leaving the constants read only by unit tests asserting each equalled its own literal. The grace period constants in RegularMaintenanceJob are used by perform, and stay.
Companion to SOLID_QUEUE_MIGRATION_PLAN.md: what migrating to Sidekiq would involve instead, grounded in this codebase (Redis allkeys-lru conflict, loss of numeric priorities, crash durability, the shared global-state concurrency blocker), plus an analysis of whether migrating to Sidekiq is easier from the original DelayedJob or from the Solid Queue branch.
…ranch) Mounts the rails/mission_control-jobs engine at /jobs to inspect and manage Solid Queue jobs. Gated behind SEEK's existing admin authentication (MissionControlJobsController) with the gem's default HTTP Basic auth disabled. The auth config is set as module attributes rather than via config.mission_control.jobs.*, since the engine copies that config into those attributes in a before_initialize hook that runs before config/initializers/*.
…nch) Adds a link to the Mission Control jobs dashboard from the admin index page, the Management section of the admin General panel, and the job queue stats view. Also relabels the stats heading to 'Total jobs waiting'.
Sets clear_finished_jobs_after to 14.days (gem default is 1.day), so finished jobs stay visible in the admin/Mission Control dashboards for two weeks before the hourly clear_finished_jobs task prunes them.
…2656) Moves sitemap:refresh out of config/schedule.rb into a SitemapRefreshJob run via config/recurring.yml (45 0 * * *, matching the old 12:45am cron time). Removes db:sessions:batch_trim entirely: sessions now live in Redis with native TTL expiry, so the task - which references the undefined ActiveRecord::SessionStore::Session - is dead code that would raise if run. The Docker-only kill-long-running-soffice.sh reaper stays on whenever/cron: it's OS-level process reaping that should run out-of-band from the job system (and more frequently than any maintenance job).
config/schedule.rb was down to a single task - the Docker-only kill-long-running-soffice.sh reaper - so the whenever DSL was no longer earning its keep. Replaced it with a static docker/seek.crontab fed straight to supercronic; setup_and_start_cron now points at that file instead of generating one via bundle exec whenever. Removed the whenever/whenever-test gems, config/schedule.rb, its integration test, and the whenever --update-crontab calls in the update-from-git scripts. supercronic is retained, so soffice reaping stays out-of-band from the job system. Cron is only started under Docker, preserving the reaper's previous Docker-only behaviour.
Adds Seek::DelayedJobMigrator, run once via a new seek:migrate_delayed_jobs_to_solid_queue upgrade task (only_once, 1.19.0), to move any rows left in the delayed_jobs table from before the Solid Queue cutover into Solid Queue's own tables. Pending/locked rows are re-enqueued preserving queue, run_at, priority and attempts (attempts folded into ActiveJob executions); stale locks are ignored since the delayed_job workers are gone. Already-failed rows are deleted. Queued reindexing jobs (ReindexAllJob/ReindexingJob) are dropped and the ReindexingQueue table is cleared, as the upgrade runs a full seek:reindex_all straight after which supersedes them. Each Solid Queue row is built directly from the raw ActiveJob payload rather than deserialising and re-enqueuing the job: re-serialising would resolve GlobalID arguments and raise DeserializationError - aborting the whole migration - for any job whose referenced record has since been deleted. Lazy deserialisation at perform time is already handled by ApplicationJob rescue_from.
workoff only reported the scheduled-to-ready dispatch count, which is zero whenever the jobs are already ready - so it looked like it had done nothing. It now also reports how many jobs were waiting and how many ran, counted via perform.active_job notifications. check was silent on success, following delayed_job's convention; it now prints the state it checked, while still exiting non-zero when jobs are overdue.
…tory (#2656) Drop comparisons with delayed_job/whenever and references to the (deleted) config/schedule.rb and the migration planning docs, so comments describe what the code does now rather than what it replaced.
…2656) The offset existed to stagger whenever's per-job `rails runner` process boots so they didn't spike server load when firing in the same minute. Solid Queue enqueues recurring tasks onto a shared worker pool instead of booting a process each, and dedups across instances via the unique (task_key, run_at) index, so that stagger no longer serves a purpose. The heavier daily jobs are still spread across 00:00-04:00 by fixed hours. Removes the now-unused regular_job_offset config setting.
…#2656) The weekly subscription digest ran on days 1, 8, 15 and 22 of the month but gathers activity over `1.week.ago`, so a day-of-month cadence leaves a gap near month boundaries: activity on ~the 23rd-24th was never included in any digest, and days 29-31 never triggered a run. Switching to "0 0 * * 0" (every Sunday) tiles the one-week window with no gap or overlap. Also adds a cron-syntax reference to the top of the file.
#1 (weekly digest month-boundary gap): mark fixed, and correct the note - it was not a migration regression; whenever also emitted 0 0 1,8,15,22 * *, verified against the gem. Fixed by moving to 0 0 * * 0 (Sunday). #2 (recurring command serialization): record why the single-threaded solid_queue_recurring worker is fine as-is - the only effect is a few minutes of overnight status-cache staleness once a day, and the default queue is no better (also threads: 1, and it would lose isolation from user-facing jobs).
…2656) Temporary: revert to fairdom/seek:main before opening a pull request.
# Conflicts: # config/schedule.rb # lib/tasks/db.rake
…2656) - Treat Errno::EPERM from Process.kill(0) as running, not stopped: the supervisor exists but is owned by another user, so it can't be signalled. - Restart the workers by the resolved pid rather than kill -TERM $(cat pidfile), which fed kill an empty argument (and a confusing error) when the pidfile was missing; now report cleanly when no workers are running. - Split each worker's comma-joined metadata['queues'] so the active-queue list and count are per-queue regardless of how queues map onto workers.
AdminController parses human-readable duration settings via Chronic.parse. chronic was only present as a transitive dependency of whenever; removing whenever dropped it, breaking the error grouping timeout parsing.
Solid Queue adds a dedicated solid_queue_recurring worker (for the command-based recurring tasks) with no delayed_job equivalent, so the deployment runs one more worker process than before. Also update the wording to match the current status message.
Contributor
There was a problem hiding this comment.
Pull request overview
This pull request migrates FAIRDOM-SEEK’s background job infrastructure from DelayedJob to Rails’ Solid Queue, adds Mission Control – Jobs for visibility/operations, and updates scheduling, admin tooling, and deployment scripts to match the new supervisor/worker model.
Changes:
- Switch ActiveJob adapter to Solid Queue, add Solid Queue DB tables, and introduce a one-off migrator for legacy
delayed_jobs. - Replace
whenever/schedule.rbwith Solid Queue recurring tasks (config/recurring.yml) and update worker topology (config/queue.yml) plus runtime scripts (bin/jobs,script/run_solid_queue.sh). - Update admin/status surfaces (restart button, job queue stats, application status output) and expand/adjust automated tests for the new job system.
Reviewed changes
Copilot reviewed 54 out of 56 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| test/unit/util_test.rb | Adds unit coverage for Solid Queue supervisor PID detection. |
| test/unit/jobs/sitemap_refresh_job_test.rb | Adds unit tests for new sitemap refresh job behavior/queue. |
| test/unit/jobs/regular_maintenance_job_test.rb | Removes tests tied to old periodic constant scheduling. |
| test/unit/jobs/cache_overflow_cleanup_job_test.rb | Removes tests tied to old periodic constant scheduling. |
| test/unit/jobs/auth_lookup_maintenance_job_test.rb | Removes tests tied to old periodic constant scheduling. |
| test/unit/delayed_job_migrator_test.rb | Adds coverage for one-off DelayedJob → Solid Queue migration logic. |
| test/unit/application_status_test.rb | Updates “running jobs” semantics to Solid Queue worker processes. |
| test/integration/schedule_test.rb | Removes Whenever schedule integration test. |
| test/integration/recurring_test.rb | Adds integration coverage for recurring.yml validity and queue coverage. |
| test/functional/statistics_controller_test.rb | Updates expected application status text output. |
| test/functional/admin_controller_test.rb | Updates admin restart/stats/failed-job clearing tests to Solid Queue. |
| script/update-from-git.sh | Updates deployment flow to stop/start Solid Queue runner instead of DJ tasks + whenever. |
| script/run_solid_queue.sh | Adds a restart-on-exit wrapper for bin/jobs supervisor. |
| script/mini-update-from-git.sh | Updates lightweight deploy script to signal Solid Queue supervisor PID. |
| script/check_worker_pids.sh | Replaces per-worker pidfile checks with Solid Queue supervisor PID check. |
| script/check_deployment.rb | Updates expected application status string for Solid Queue workers. |
| lib/tasks/seek_workers.rake | Removes old DelayedJob worker rake task namespace. |
| lib/tasks/seek_upgrades.rake | Adds one-off upgrade step to migrate remaining delayed_jobs. |
| lib/tasks/jobs.rake | Redefines jobs:* rake tasks to operate against Solid Queue. |
| lib/tasks/db.rake | Minor formatting cleanup. |
| lib/seek/workers.rb | Removes DelayedJob worker orchestration module. |
| lib/seek/util.rb | Adds Solid Queue supervisor PID helper for admin/scripts. |
| lib/seek/delayed_job_migrator.rb | Implements the delayed_jobs → Solid Queue migration logic. |
| lib/seek/config_setting_attributes.yml | Removes regular_job_offset setting (no longer used without Whenever). |
| Gemfile.lock | Adds Solid Queue + Mission Control – Jobs dependencies; removes whenever/daemons. |
| Gemfile | Declares solid_queue, mission_control-jobs, and adds chronic explicitly. |
| Dockerfile | Ensures Solid Queue runner script is executable in image. |
| docker/start_workers.sh | Switches container worker startup to Solid Queue runner. |
| docker/shared_functions.sh | Switches cron setup to static docker crontab file. |
| docker/seek.crontab | Adds static supercronic crontab for OS-level soffice reaping only. |
| docker/entrypoint.sh | Starts Solid Queue runner instead of DJ workers; keeps cron startup. |
| docker-compose.yml | Updates worker service description for Solid Queue. |
| db/schema.rb | Adds Solid Queue tables/foreign keys and bumps schema version. |
| db/migrate/20260715145804_create_solid_queue_tables.rb | Introduces Solid Queue schema as a standard migration. |
| config/schedule.rb | Removes Whenever schedule definition. |
| config/routes.rb | Mounts Mission Control – Jobs engine and updates admin restart route. |
| config/recurring.yml | Adds recurring task schedule definitions for Solid Queue scheduler. |
| config/queue.yml | Adds per-queue worker topology with feature-flag gating and threads=1 constraint. |
| config/initializers/solid_queue.rb | Configures supervisor pidfile path and finished-job retention window. |
| config/initializers/mission_control.rb | Configures Mission Control – Jobs to use SEEK admin auth (no HTTP Basic). |
| config/application.rb | Switches ActiveJob adapter to :solid_queue. |
| bin/jobs | Adds bin/jobs entrypoint for Solid Queue CLI. |
| app/views/statistics/application_status.text.erb | Updates status text to report Solid Queue worker processes. |
| app/views/admin/stats/_job_queue.html.erb | Reworks admin job queue panel to use Solid Queue jobs/failures + dashboard link. |
| app/views/admin/index.html.erb | Adds link to job queue dashboard. |
| app/views/admin/_restart_buttons.html.erb | Updates restart/status UI to target Solid Queue supervisor and list active queues. |
| app/views/admin/_general.html.erb | Adds job queue dashboard link to admin nav. |
| app/models/application_status.rb | Updates running job count logic to Solid Queue worker heartbeats. |
| app/jobs/sitemap_refresh_job.rb | Adds Solid Queue-scheduled job to refresh sitemap + ping engines. |
| app/jobs/regular_maintenance_job.rb | Removes RUN_PERIOD constant; schedule now defined in recurring.yml. |
| app/jobs/life_monitor_status_job.rb | Removes PERIOD constant; schedule now defined in recurring.yml. |
| app/jobs/cache_overflow_cleanup_job.rb | Removes RUN_PERIOD constant; schedule now defined in recurring.yml. |
| app/jobs/auth_lookup_maintenance_job.rb | Removes RUN_PERIOD constant; schedule now defined in recurring.yml. |
| app/controllers/mission_control_jobs_controller.rb | Adds base controller enforcing SEEK admin auth for Mission Control dashboard. |
| app/controllers/admin_controller.rb | Replaces delayed job restart/cleanup actions with Solid Queue equivalents. |
| AGENTS.md | Updates repo agent guidance to reflect Solid Queue commands/architecture. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
- Bump the Solid Queue tables migration to ActiveRecord::Migration[7.2] to match the rest of the app's migrations. - Scope jobs:workoff's 'ready to run' count to the selected QUEUES so it matches the message. - Fix the mis-indented closing paren in ApplicationStatus#refresh. - Reword the empty worker-status line to 'No background job workers running' for consistency with its heading.
Replace the hand-rolled kill -TERM $(cat ...pid) invocations in the deployment scripts with rake tasks: - jobs:stop TERMs the run_solid_queue.sh runner loop (or the bare supervisor) so it stays down; used by update-from-git.sh. - jobs:restart TERMs the supervisor so the runner respawns it with new code; used by mini-update-from-git.sh.
…pidfile (#2656) Mirror SolidQueue.supervisor_pidfile so Seek.solid_queue_runner_pid reads the path from config rather than hard-coding it, and cross-reference it from script/run_solid_queue.sh, which writes the same location.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Migrates SEEK's background job processing from DelayedJob to Solid Queue, and adds a Mission Control – Jobs dashboard for inspecting the queues. Closes #2656.
Solid Queue's tables live in the primary database (shared, not a separate queue DB). The
delayed_job_active_recordgem and its tables are retained so existing jobs can be migrated across (see the migrator below); they can be removed in 1.20.0.What changed
config.active_job.queue_adapterflipped to:solid_queue; Solid Queue tables added via a regular migration (db/migrate/20260715145804_create_solid_queue_tables.rb).config/queue.yml) — one worker per queue, eachthreads: 1(a hard constraint whileUser.current_user/$authorization_checks_disabledremain process-global), feature-flag gated to match the previous per-queue setup. Started viabin/jobs;script/run_solid_queue.shwraps it with restart-on-exit for deployment.config/recurring.yml) — replaces thewhenever/schedule.rbcron generation. Thewhenevergem is removed; a static crontab (docker/seek.crontab) fed to supercronic now covers the one OS-level shell task (the soffice reaper). Schedules documented in-file with a cron-syntax reference.seek:upgrademigrates any leftoverdelayed_jobsrows into Solid Queue, preserving serialized arguments, foldingattemptsintoexecutions, and tolerating dangling GlobalIDs./jobs, gated behind SEEK's own admin auth (not the gem's HTTP Basic auth). Finished jobs retained for 14 days.jobs:work/workoff/clear/checkredefined against Solid Queue.Behaviour changes worth noting
0 0 * * 0(every Sunday). The oldwhenever-generated0 0 1,8,15,22 * *left a coverage gap near month boundaries against the job's fixed1.week.agowindow — a long-standing bug, not a regression, now fixed.db:sessions:batch_trimdropped (sessions live in Redis withallkeys-lrunow).Testing
Covered by
test/integration/recurring_test.rb,test/unit/delayed_job_migrator_test.rb, updated admin/status tests, and new unit/functional coverage for the supervisor-pid and worker-restart edge cases.Draft — still open
delayed_job_active_recordgem and its tables are retained so existing DelayedJob rows can be migrated into Solid Queue; they can be removed in 1.20.0 once no instance still needs to migrate.solid_queue_recurringworker thread (only effect is a few minutes of overnight status-cache staleness while the daily bioschema dump runs).