Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 6 additions & 2 deletions .env.docker
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,12 @@ REDIS_URL=redis://redis:6379/0
# Configuration for asynchronous job processing
# ==============================================================================
#BACKGROUND_JOBS= # Enable/disable background job processing
#RESQUE_NAMESPACE= # Namespace for Resque jobs
#WORKERS_COUNT= # Number of worker processes
#SOLID_QUEUE_IN_PUMA= # Run Solid Queue supervisor inside Puma (for single-server deploys)
#JOB_CONCURRENCY=2 # Number of Solid Queue worker processes
#JOB_THREADS=3 # Number of threads per worker process
#JOB_POLLING_INTERVAL=0.1 # Worker polling interval in seconds
#JOB_DISPATCHER_POLLING_INTERVAL=1 # Dispatcher polling interval in seconds
#JOB_DISPATCHER_BATCH_SIZE=500 # Number of jobs dispatched per batch

# ==============================================================================
# GOOGLE API INTEGRATION
Expand Down
8 changes: 6 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,12 @@ AWS_S3_PREVIEW_FOLDER= # S3 folder for storing previews
# Configuration for asynchronous job processing
# ==============================================================================
BACKGROUND_JOBS= # Enable/disable background job processing
RESQUE_NAMESPACE= # Namespace for Resque jobs
WORKERS_COUNT= # Number of worker processes
SOLID_QUEUE_IN_PUMA= # Run Solid Queue supervisor inside Puma (for single-server deploys)
JOB_CONCURRENCY= # Number of Solid Queue worker processes (default: 2)
JOB_THREADS= # Number of threads per worker process (default: 3)
JOB_POLLING_INTERVAL= # Worker polling interval in seconds (default: 0.1)
JOB_DISPATCHER_POLLING_INTERVAL= # Dispatcher polling interval in seconds (default: 1)
JOB_DISPATCHER_BATCH_SIZE= # Number of jobs dispatched per batch (default: 500)

# ==============================================================================
# GOOGLE API INTEGRATION
Expand Down
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ Jobs are in `app/jobs/` and use Resque with ActiveJob:
- `MaterialGeneratePdfJob`: PDF generation
- `MaterialGenerateGdocJob`: Google Doc generation

All jobs inherit from `ApplicationJob` with retry logic via `activejob-retry`.
All jobs inherit from `ApplicationJob` with retry logic via Rails' built-in `retry_on`.

### Template System (lib/doc_template)

Expand Down
4 changes: 1 addition & 3 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,8 @@ gem "devise", "~> 5.0"
# Background Jobs & Queue
gem "redis"
gem "hiredis-client"
gem "resque"
gem "resque-scheduler", "~> 5.0"
gem "activejob-retry", "~> 0.6.3"
gem "concurrent-ruby", "~> 1.3"
gem "mission_control-jobs"

# Search & Full-text
gem "elasticsearch-model", "~> 8.0"
Expand Down
56 changes: 15 additions & 41 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,6 @@ GEM
activejob (8.1.1)
activesupport (= 8.1.1)
globalid (>= 0.3.6)
activejob-retry (0.6.3)
activejob (>= 4.2)
activesupport (>= 4.2)
activemodel (8.1.1)
activesupport (= 8.1.1)
activerecord (8.1.1)
Expand Down Expand Up @@ -320,6 +317,10 @@ GEM
image_processing (1.14.0)
mini_magick (>= 4.9.5, < 6)
ruby-vips (>= 2.0.17, < 3)
importmap-rails (2.2.3)
actionpack (>= 6.0.0)
activesupport (>= 6.0.0)
railties (>= 6.0.0)
iniparse (1.5.0)
io-console (0.8.2)
irb (1.17.0)
Expand Down Expand Up @@ -387,15 +388,22 @@ GEM
mini_mime (1.1.5)
minitest (6.0.1)
prism (~> 1.5)
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.53.0)
redis (~> 5)
mono_logger (1.1.2)
msgpack (1.8.0)
multi_json (1.19.1)
multi_xml (0.8.1)
bigdecimal (>= 3.1, < 5)
mustermann (3.0.4)
ruby2_keywords (~> 0.0.1)
mutex_m (0.3.0)
net-http (0.9.1)
uri (>= 0.11.1)
Expand Down Expand Up @@ -468,10 +476,6 @@ GEM
rack (3.2.4)
rack-mini-profiler (4.0.1)
rack (>= 1.2.0)
rack-protection (4.2.1)
base64 (>= 0.1.0)
logger (>= 1.6.0)
rack (>= 3.0.0, < 4)
rack-session (2.1.1)
base64 (>= 0.1.0)
rack (>= 3.0.0)
Expand Down Expand Up @@ -534,8 +538,6 @@ GEM
redis-client (>= 0.22.0)
redis-client (0.26.4)
connection_pool
redis-namespace (1.11.0)
redis (>= 4)
regexp_parser (2.11.3)
reline (0.6.3)
io-console (~> 0.5)
Expand All @@ -546,21 +548,6 @@ GEM
responders (3.2.0)
actionpack (>= 7.0)
railties (>= 7.0)
resque (3.0.0)
base64 (~> 0.1)
logger
mono_logger (~> 1)
multi_json (~> 1.0)
redis (>= 4.0)
redis-namespace (~> 1.6)
sinatra (>= 2.0)
resque-scheduler (5.0.0)
base64 (~> 0.1)
logger
mono_logger (~> 1.0)
redis (>= 4.0)
resque (>= 3.0)
rufus-scheduler (~> 3.2, != 3.3)
rest-client (2.1.0)
http-accept (>= 1.7.0, < 2.0)
http-cookie (>= 1.0.2, < 2.0)
Expand Down Expand Up @@ -618,10 +605,7 @@ GEM
ruby-vips (2.2.5)
ffi (~> 1.12)
logger
ruby2_keywords (0.0.5)
rubyzip (2.4.1)
rufus-scheduler (3.9.2)
fugit (~> 1.1, >= 1.11.1)
sanitize (7.0.0)
crass (~> 1.0.2)
nokogiri (>= 1.16.8)
Expand Down Expand Up @@ -652,13 +636,6 @@ GEM
simplecov_json_formatter (~> 0.1)
simplecov-html (0.13.2)
simplecov_json_formatter (0.1.4)
sinatra (4.2.1)
logger (>= 1.6.0)
mustermann (~> 3.0)
rack (>= 3.0.0, < 4)
rack-protection (= 4.2.1)
rack-session (>= 2.0.0, < 3)
tilt (~> 2.0)
solid_cable (3.0.12)
actioncable (>= 7.2)
activejob (>= 7.2)
Expand Down Expand Up @@ -720,7 +697,6 @@ GEM
thruster (0.1.16-aarch64-linux)
thruster (0.1.16-arm64-darwin)
thruster (0.1.16-x86_64-linux)
tilt (2.7.0)
timeout (0.6.0)
traceroute (0.8.1)
rails (>= 3.0.0)
Expand Down Expand Up @@ -783,7 +759,6 @@ PLATFORMS

DEPENDENCIES
active_model_serializers (~> 0.10.16)
activejob-retry (~> 0.6.3)
acts-as-taggable-on (~> 13.0)
acts_as_list (~> 1.0)
airbrake (~> 13.0)
Expand Down Expand Up @@ -825,6 +800,7 @@ DEPENDENCIES
lt-google-api (~> 0.4)
lt-lcms (~> 0.7)
mini_magick (~> 5.3)
mission_control-jobs
mock_redis
nokogiri (~> 1.19)
oj (~> 3.16)
Expand All @@ -840,8 +816,6 @@ DEPENDENCIES
ransack (~> 4.2)
rbs_rails
redis
resque
resque-scheduler (~> 5.0)
rest-client (~> 2.1, >= 2.1.0)
retriable (~> 3.1)
rspec-rails (~> 8.0)
Expand Down
19 changes: 5 additions & 14 deletions MIGRATION_PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -294,9 +294,10 @@ TO: app/controllers/*.rb

#### 5.2. Job Concerns
- [x] Migrate concerns from `app/jobs/concerns/`
- NestedResqueJob
- RetryDelayed
- RetrySimple
- NestedResqueJob → NestedJobTracker (uses JobResult DB table instead of Redis)
- RetryDelayed → rewritten with native Rails `retry_on` / `discard_on`
- RetrySimple → removed (unused)
- Removed `activejob-retry` gem dependency

#### 5.3. Queue Configuration
- [x] Decide: Resque vs Solid Queue (chose Resque)
Expand All @@ -305,17 +306,7 @@ TO: app/controllers/*.rb
- [x] Migrate lib/resque_job.rb
- [x] Migrate lib/tasks/resque.rake

**Option A: Keep Resque**
```ruby
# config/application.rb
config.active_job.queue_adapter = :resque

# lib/tasks/resque.rake
require 'resque/tasks'
require 'resque/scheduler/tasks'
```

**Option B: Migrate to Solid Queue (Rails 8.1)**
**Background Queue with Solid Queue (Rails 8.1)**
```ruby
# config/application.rb
config.active_job.queue_adapter = :solid_queue
Expand Down
3 changes: 1 addition & 2 deletions Procfile
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
worker: bundle exec rake resque:work QUEUE=*
scheduler: bundle exec rake resque:scheduler
worker: bundle exec rake solid_queue:start
4 changes: 2 additions & 2 deletions app/jobs/base_bundle_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
# Shouldn't be called itself, constants should be defined in inherited classes
#
class BaseBundleJob < ApplicationJob
include ResqueJob
include NestedResqueJob
include JobTracker
include NestedJobTracker

def perform(_entry_id, _options = {})
raise NotImplementedError
Expand Down
4 changes: 2 additions & 2 deletions app/jobs/concerns/document_rescuable_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ module DocumentRescuableJob
options = (arguments[1] || {}).with_indifferent_access
unless options[:preview]
document = Document.find(document_id)
document.reload.with_lock do
document.with_lock do
data = document.links[self.class::LINK_KEY]&.slice("preview") || {}
document.update_columns(links: document.reload.links.merge(self.class::LINK_KEY => data))
document.update_columns(links: document.links.merge(self.class::LINK_KEY => data))
end
store_result({ ok: false,
link: document_path(document_id),
Expand Down
2 changes: 1 addition & 1 deletion app/jobs/concerns/material_rescuable_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ module MaterialRescuableJob
material = MaterialPresenter.new(material_id)
options = (arguments[1] || {}).with_indifferent_access
unless options[:preview]
material.reload.with_lock do
material.with_lock do
material.update_columns(links: material.links.merge(self.class::LINK_KEY => {}))
end
store_result({ ok: false,
Expand Down
36 changes: 36 additions & 0 deletions app/jobs/concerns/nested_job_tracker.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# frozen_string_literal: true

module NestedJobTracker
extend ActiveSupport::Concern

class_methods do
def queued_or_running_nested?(job_id, current_job_id = "-1")
check_child = ->(j) { j["arguments"][1]&.dig("initial_job_id") == job_id && j["job_id"] != current_job_id }
job_klasses = self::NESTED_JOBS + [name]
job_klasses.each do |job_klass|
queued = find_in_queue_by_payload(job_klass, &check_child) ||
find_in_working_by_payload(job_klass, &check_child)
return true if queued.present?
end
false
end

def status_nested(jid)
self_status = status(jid)
return self_status unless self_status == :done
return :running if queued_or_running_nested?(jid)

:done
end

def fetch_result_nested(jid)
JobResult.for_parent(jid).pluck(:result)
end
end

private

def initial_job_id
@initial_job_id ||= options[:initial_job_id].presence || job_id
end
end
56 changes: 0 additions & 56 deletions app/jobs/concerns/nested_resque_job.rb

This file was deleted.

Loading