Skip to content
Open
Show file tree
Hide file tree
Changes from 46 commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
a25a16d
Add migration plan for moving from DelayedJob to Solid Queue (#2656)
stuzart Jul 15, 2026
f2c1f11
Decouple Solid Queue cutover from migrating historical delayed_jobs r…
stuzart Jul 15, 2026
7693892
Resolve database and thread-safety open questions in plan (#2656)
stuzart Jul 15, 2026
7cc8ec1
Complete most of Phase 0: thread-safety audit and version check (#2656)
stuzart Jul 15, 2026
a0d28b0
Decide queue topology for Phase 0: replicate 1:1, consolidate later (…
stuzart Jul 15, 2026
de7b1e2
Add Solid Queue alongside DelayedJob, no cutover yet (#2656)
stuzart Jul 15, 2026
6a877e8
Complete Phase 2: local/dev verification of Solid Queue (#2656)
stuzart Jul 15, 2026
fa9c3c1
Replace DelayedJob worker infra with Solid Queue (#2656)
stuzart Jul 15, 2026
4c1e8af
Cutover: flip ActiveJob adapter to Solid Queue (#2656)
stuzart Jul 16, 2026
ec63256
Add manual testing guide for Solid Queue (#2656)
stuzart Jul 16, 2026
a2406eb
Fix db:setup FK drop-order failure on MySQL (#2656)
stuzart Jul 16, 2026
24060f0
Fix inflated background job worker count, list active queues (#2656)
stuzart Jul 16, 2026
be39297
Fix double-scheduling and missing recurring-job worker (#2656)
stuzart Jul 16, 2026
91b4baf
Reduce Solid Queue worker polling interval to cut idle DB load (#2656)
stuzart Jul 16, 2026
64db398
Make Solid Queue code comments self-contained (#2656)
stuzart Jul 16, 2026
700b562
Apply job offset to hour-frequency recurring jobs (#2656)
stuzart Jul 16, 2026
a0d756a
Fix failed-job error display and N+1 in admin job queue panel (#2656)
stuzart Jul 16, 2026
eca6c08
Merge branch 'main' into solid-queue-2656
stuzart Jul 17, 2026
3a81fda
Move CacheOverflowCleanupJob to recurring.yml (#2656)
stuzart Jul 17, 2026
ffa164b
Remove unused job scheduling period constants (#2656)
stuzart Jul 17, 2026
8b2b9b9
Add alternative Sidekiq migration plan and comparison (#2656)
stuzart Jul 21, 2026
6d8918c
Add Mission Control - Jobs dashboard (experimental, mission-control b…
stuzart Jul 21, 2026
e18a253
Link to the Job queue dashboard from admin pages (mission-control bra…
stuzart Jul 21, 2026
5bea66a
Keep finished Solid Queue jobs for 14 days (mission-control branch)
stuzart Jul 21, 2026
fd459db
Simplify job queue stats heading to 'Total jobs waiting' (#2656)
stuzart Jul 21, 2026
e165e90
Move sitemap refresh to a recurring job, drop dead session-trim task …
stuzart Jul 21, 2026
869a0bb
Update job statistics test for "Total jobs waiting" heading text (#2656)
stuzart Jul 21, 2026
70d3158
Remove whenever gem, feed static crontab to supercronic (#2656)
stuzart Jul 21, 2026
83781d3
Merge branch 'mission-control' into solid-queue-2656
stuzart Jul 21, 2026
658fe0d
Migrate leftover delayed_jobs into Solid Queue on upgrade (#2656)
stuzart Jul 21, 2026
b9947cd
Add code review notes for the Solid Queue migration (#2656)
stuzart Jul 22, 2026
dab475d
Redefine the jobs:* rake tasks against Solid Queue (#2656)
stuzart Jul 22, 2026
86d6720
Report what jobs:workoff and jobs:check actually did (#2656)
stuzart Jul 22, 2026
2f3cc5c
Merge branch 'main' into solid-queue-2656
stuzart Jul 23, 2026
d17d58a
Reword code comments to describe present behaviour, not migration his…
stuzart Jul 24, 2026
d108009
Drop the regular_job_offset minute-stagger from recurring schedules (…
stuzart Jul 24, 2026
9bfe185
Fix weekly digest coverage gap; document cron format in recurring.yml…
stuzart Jul 24, 2026
630f9f8
Record review findings #1 (fixed) and #2 (not a real problem) (#2656)
stuzart Jul 24, 2026
2499c63
Point docker-compose at fairdom/seek:solid-queue for local testing (#…
stuzart Jul 24, 2026
93ad630
Merge branch 'main' into solid-queue-2656
stuzart Aug 18, 2026
8efa591
Reword Mission Control comments to describe present state (#2656)
stuzart Aug 18, 2026
16fa53c
Harden Solid Queue supervisor status and worker restart edge cases (#…
stuzart Aug 18, 2026
31f7a3d
Record review finding #3 as fixed (#2656)
stuzart Aug 18, 2026
f94595f
Revert docker-compose image to fairdom/seek:main; remove working docs…
stuzart Aug 20, 2026
1c41e54
Depend on chronic explicitly (#2656)
stuzart Aug 20, 2026
1f41606
Expect 8 background job workers in deployment check (#2656)
stuzart Aug 20, 2026
f2dc750
Address Copilot review comments on Solid Queue PR (#2656)
stuzart Aug 21, 2026
08188e8
Log when the Solid Queue supervisor starts, not just when it exits (#…
stuzart Aug 24, 2026
f543eb1
Add jobs:stop and jobs:restart rake tasks (#2656)
stuzart Aug 24, 2026
e303898
removed some unnecessary gemfile comments
stuzart Aug 24, 2026
621c432
Define SolidQueue.runner_pidfile as the single source for the runner …
stuzart Aug 24, 2026
8c8577d
Restore daemonized seek:workers:start/stop/restart/status tasks (#2656)
stuzart Sep 3, 2026
20a63c5
Show a restarting notice with a refresh link after restarting workers…
stuzart Sep 3, 2026
2404283
Take the worker log path from Rails config, not a hand-built path (#2…
stuzart Sep 3, 2026
21225c1
Keep the news feed refresh schedule a valid cron for any cache timeou…
stuzart Sep 4, 2026
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
5 changes: 2 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ FAIRDOM-SEEK (v1.18) is a Rails 7.2 / Ruby 3.3 web platform for sharing scientif

```bash
bundle exec rails server # Start the app (default: port 3000)
bundle exec rake seek:workers:start # Start delayed_job background workers
bundle exec rake seek:workers:stop
bundle exec bin/jobs # Start Solid Queue background workers
```

### Testing
Expand Down Expand Up @@ -116,7 +115,7 @@ The REST API uses `active_model_serializers` with `BaseSerializer` (`app/seriali

### Background Jobs

All async work uses `delayed_job` (ActiveRecord backend). Jobs are in `app/jobs/`. Start/stop workers via `rake seek:workers:start/stop`. Key jobs: `AuthLookupUpdateJob`, `ReindexingJob`, subscription email jobs, RDF generation.
All async work uses `Solid Queue` (`solid_queue` gem, tables in the primary database). Jobs are in `app/jobs/`. Worker/dispatcher/scheduler processes are started via `bin/jobs` (see `config/queue.yml` for topology, `config/recurring.yml` for scheduled jobs); `script/run_solid_queue.sh` wraps this with automatic restart-on-exit for deployment. Key jobs: `AuthLookupUpdateJob`, `ReindexingJob`, subscription email jobs, RDF generation. (Migrated from `delayed_job` - the `delayed_job_active_record` gem and its tables remain installed as a rollback safety net for now.)

### Semantic / RDF

Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ RUN mkdir -p /var/www

# Fix permissions
RUN chown -R www-data:www-data /var/www config docker db/schema.rb public solr sqlite3-db
RUN chmod -R 755 docker/upgrade.sh docker/start_workers.sh
RUN chmod -R 755 docker/upgrade.sh docker/start_workers.sh script/run_solid_queue.sh

# Python dependencies from requirements.txt
RUN PYTHON_VERSION=$(cat .python-version) && \
Expand Down
7 changes: 4 additions & 3 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ gem 'bundler'
gem 'caxlsx'
gem 'caxlsx_rails'
gem 'cff'
gem 'chronic' # AdminController parses human-readable duration settings (e.g. error grouping timeout) with Chronic
gem 'citeproc-ruby'
gem 'commonmarker'
gem 'country_select'
gem 'csl-styles'
gem 'csv'
gem 'daemons'
gem 'delayed_job_active_record'
gem 'docsplit', git: 'https://github.com/documentcloud/docsplit.git'
gem 'doorkeeper'
Expand Down Expand Up @@ -55,6 +55,8 @@ gem 'loofah'
gem 'mechanize'
gem 'metainspector'
gem 'mimemagic'
# Experimental: web dashboard for inspecting/managing Solid Queue jobs (on the mission-control branch)
gem 'mission_control-jobs'
gem 'mutex_m'
gem 'mysql2'
gem 'net-ftp'
Expand Down Expand Up @@ -99,13 +101,13 @@ gem 'sass-rails'
gem 'seedbank'
gem 'simple-spreadsheet-extractor'
gem 'sitemap_generator'
gem 'solid_queue'
gem 'sprockets-rails'
gem 'sqlite3'
gem 'sunspot_rails'
gem 'terrapin'
gem 'terser'
gem 'validate_url'
gem 'whenever', require: false
gem 'will_paginate'
gem 'yaml_db'
gem 'zenodo-client', git: 'https://github.com/seek4science/zenodo-client.git'
Expand Down Expand Up @@ -145,7 +147,6 @@ group :test do
gem 'test-prof'
gem 'selenium-webdriver', '~> 4.44'
gem 'vcr'
gem 'whenever-test'
end

group :test, :development do
Expand Down
43 changes: 35 additions & 8 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,6 @@ GEM
csl-styles (2.0.1)
csl (~> 2.0)
csv (3.3.3)
daemons (1.4.1)
database_cleaner (2.1.0)
database_cleaner-active_record (>= 2, < 3)
database_cleaner-active_record (2.2.0)
Expand Down Expand Up @@ -265,6 +264,8 @@ GEM
erb (6.0.4)
erubi (1.13.1)
erubis (2.7.0)
et-orbi (1.4.0)
tzinfo
exception_notification (5.0.0)
actionmailer (>= 7.1, < 9)
activesupport (>= 7.1, < 9)
Expand Down Expand Up @@ -300,6 +301,9 @@ GEM
ffi (1.17.1-x86_64-linux-gnu)
flamegraph (0.9.5)
forwardable (1.3.3)
fugit (1.13.0)
et-orbi (~> 1.4)
raabro (~> 1.4)
gem-licenses (0.2.2)
gitlab_omniauth-ldap (2.2.0)
net-ldap (~> 0.16)
Expand Down Expand Up @@ -338,6 +342,10 @@ GEM
concurrent-ruby (~> 1.0)
i18n-js (3.9.2)
i18n (>= 0.6.6)
importmap-rails (2.2.3)
actionpack (>= 6.0.0)
activesupport (>= 6.0.0)
railties (>= 6.0.0)
indefinite_article (0.2.5)
activesupport
io-console (0.8.2)
Expand Down Expand Up @@ -499,6 +507,16 @@ GEM
builder
minitest (>= 5.0)
ruby-progressbar
mission_control-jobs (1.1.0)
actioncable (>= 7.1)
actionpack (>= 7.1)
activejob (>= 7.1)
activerecord (>= 7.1)
importmap-rails (>= 1.2.1)
irb (~> 1.13)
railties (>= 7.1)
stimulus-rails
turbo-rails
mock_redis (0.55.0)
redis (~> 5)
msgpack (1.8.2)
Expand Down Expand Up @@ -626,6 +644,7 @@ GEM
puma (7.2.1)
nio4r (~> 2.0)
pyu-ruby-sasl (0.0.3.3)
raabro (1.4.0)
racc (1.8.1)
rack (3.2.6)
rack-attack (6.7.0)
Expand Down Expand Up @@ -942,6 +961,13 @@ GEM
snaky_hash (2.0.6)
hashie (>= 0.1.0, < 6)
version_gem (>= 1.1.8, < 3)
solid_queue (1.4.0)
activejob (>= 7.1)
activerecord (>= 7.1)
concurrent-ruby (>= 1.3.1)
fugit (~> 1.11)
railties (>= 7.1)
thor (>= 1.3.1)
sparql (3.3.2)
builder (~> 3.2, >= 3.2.4)
ebnf (~> 2.5)
Expand All @@ -967,6 +993,8 @@ GEM
sqlite3 (2.9.5-arm64-darwin)
sqlite3 (2.9.5-x86_64-linux-gnu)
stackprof (0.2.27)
stimulus-rails (1.3.4)
railties (>= 6.0.0)
stringio (3.2.0)
sunspot (2.7.1)
bigdecimal
Expand Down Expand Up @@ -1001,6 +1029,9 @@ GEM
date
timeout (0.6.1)
tsort (0.2.0)
turbo-rails (2.0.23)
actionpack (>= 7.1.0)
railties (>= 7.1.0)
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
ucf (3.0.0)
Expand Down Expand Up @@ -1040,10 +1071,6 @@ GEM
base64
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.5)
whenever (1.0.0)
chronic (>= 0.6.3)
whenever-test (1.0.1)
whenever
will_paginate (4.0.1)
yaml-ld (0.0.3)
json-ld (~> 3.3)
Expand Down Expand Up @@ -1087,12 +1114,12 @@ DEPENDENCIES
caxlsx
caxlsx_rails
cff
chronic
citeproc-ruby
commonmarker
country_select
csl-styles
csv
daemons
database_cleaner
delayed_job_active_record
docsplit!
Expand Down Expand Up @@ -1130,6 +1157,7 @@ DEPENDENCIES
minitar
minitest (~> 5.4)
minitest-reporters
mission_control-jobs
mock_redis
mutex_m
mysql2
Expand Down Expand Up @@ -1193,6 +1221,7 @@ DEPENDENCIES
simple-spreadsheet-extractor
simplecov
sitemap_generator
solid_queue
sprockets-rails
sqlite3
stackprof
Expand All @@ -1207,8 +1236,6 @@ DEPENDENCIES
vcr
web-console
webmock
whenever
whenever-test
will_paginate
yaml_db
zenodo-client!
Expand Down
30 changes: 11 additions & 19 deletions app/controllers/admin_controller.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
require 'delayed/command'

class AdminController < ApplicationController
include CommonSweepers

Expand Down Expand Up @@ -376,10 +374,15 @@ def restart_server
redirect_with_status(error, 'server')
end

def restart_delayed_job
command = "bundle exec rake seek:workers:restart"
error = execute_command(command)
redirect_with_status(error, 'background job workers')
def restart_job_workers
pid = Seek::Util.solid_queue_supervisor_pid
if pid
error = execute_command("kill -TERM #{pid}")
redirect_with_status(error, 'background job workers')
else
flash[:error] = 'There are no background job workers running to restart.'
redirect_to action: :show
end
end

def clear_cache
Expand All @@ -390,17 +393,6 @@ def clear_cache
end
end

# give it up to 5 seconds to start up, otherwise the page reloads too quickly and says it is not running
def wait_for_delayed_job_to_start
sleep(0.5)
pid = Daemons::PidFile.new("#{Rails.root}/tmp/pids", 'delayed_job.0')
count = 0
while !pid.running? && (count < 10)
sleep(0.5)
count += 1
end
end

def edit_tag
@tag = TextValue.find(params[:id])
if request.post?
Expand Down Expand Up @@ -627,9 +619,9 @@ def array_remove_at(array, index)
return array.slice(0,index) + array.slice(index+1,array.length)
end

# this destroys any failed Delayed::Jobs
# this destroys any failed SolidQueue::Jobs
def clear_failed_jobs
Delayed::Job.where('failed_at IS NOT NULL').destroy_all
SolidQueue::Job.failed.destroy_all
respond_to do |format|
format.json { head :ok }
end
Expand Down
8 changes: 8 additions & 0 deletions app/controllers/mission_control_jobs_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Base controller for the Mission Control - Jobs dashboard (mounted at /jobs). The engine's own
# controllers inherit from this, so gating it here reuses SEEK's existing admin authentication
# (login_required + is_user_admin_auth) rather than the gem's default HTTP Basic auth - only a
# logged-in admin can reach the queue dashboard.
class MissionControlJobsController < ApplicationController
before_action :login_required
before_action :is_user_admin_auth
end
2 changes: 0 additions & 2 deletions app/jobs/auth_lookup_maintenance_job.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
class AuthLookupMaintenanceJob < ApplicationJob
RUN_PERIOD = 8.hours.freeze

queue_as QueueNames::AUTH_LOOKUP
queue_with_priority 3

Expand Down
2 changes: 0 additions & 2 deletions app/jobs/cache_overflow_cleanup_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
# rising is the signal that the shared Redis instance is under enough memory pressure that session
# keys could, in principle, start being evicted too.
class CacheOverflowCleanupJob < ApplicationJob
RUN_PERIOD = 1.day.freeze

def perform
Rails.cache.cleanup
log_redis_memory_stats
Expand Down
2 changes: 0 additions & 2 deletions app/jobs/life_monitor_status_job.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
class LifeMonitorStatusJob < ApplicationJob
PERIOD = 1.day.freeze

def perform(offset = 0)
return unless Seek::Config.life_monitor_enabled
token = LifeMonitor::Oauth2::Client.new.get_token
Expand Down
3 changes: 1 addition & 2 deletions app/jobs/regular_maintenance_job.rb
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
# A job that runs regularly and performs certain general maintenance tasks
# the jobs is run periodically according to RUN_PERIOD
# the job is run periodically, according to the schedule set in config/recurring.yml
# short running, simple, maintenance operations can be added here, complex, or longer running operations should spawn a
# new job specific to the operation

require 'rake'

class RegularMaintenanceJob < ApplicationJob
RUN_PERIOD = 4.hours.freeze
REMOVE_DANGLING_BLOB_GRACE_PERIOD = 8.hours.freeze
REMOVE_DELETED_BLOB_GRACE_PERIOD = 24.hours.freeze
REPO_GRACE_PERIOD = 8.hours.freeze
Expand Down
8 changes: 8 additions & 0 deletions app/jobs/sitemap_refresh_job.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Regenerates the XML sitemap from config/sitemap.rb and pings search engines. Scheduled
# periodically via config/recurring.yml. verbose: false keeps it quiet in the job logs.
class SitemapRefreshJob < ApplicationJob
def perform
SitemapGenerator::Interpreter.run(verbose: false)
SitemapGenerator::Sitemap.ping_search_engines
end
end
3 changes: 2 additions & 1 deletion app/models/application_status.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ class ApplicationStatus < ApplicationRecord
before_create :validate_singleton

def refresh
alive_since = SolidQueue.process_alive_threshold.ago
update(
running_jobs: Seek::Util.delayed_job_pids.count
running_jobs: SolidQueue::Process.where(kind: 'Worker').where('last_heartbeat_at > ?', alive_since).count
)
Comment thread
stuzart marked this conversation as resolved.
Outdated
end

Expand Down
1 change: 1 addition & 0 deletions app/views/admin/_general.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
<h4>Management</h4>
<ul>
<li><%= link_to 'Dashboard', dashboard_admin_stats_path -%></li>
<li><%= link_to 'Job queue dashboard', mission_control_jobs_path -%></li>
<li><%= link_to 'Announcements', site_announcements_path -%></li>
<li><%= link_to 'Manage Assay Types', suggested_assay_types_path %></li>
<li><%= link_to 'Manage Technology Types', suggested_technology_types_path %></li>
Expand Down
Loading
Loading