Browse Source

Add ADR

pull/51/head
baarkerlounger 4 years ago
parent
commit
9ecff2e2a2
  1. 2
      Gemfile
  2. 19
      Gemfile.lock
  3. 2
      config/application.rb
  4. 29
      db/migrate/20211018125238_create_good_jobs.rb
  5. 24
      db/schema.rb
  6. 21
      doc/adr/adr-008-queue-job-processing.md

2
Gemfile

@ -25,6 +25,8 @@ gem "govuk_design_system_formbuilder"
gem "hotwire-rails" gem "hotwire-rails"
# Soft delete ActiveRecords objects # Soft delete ActiveRecords objects
gem "discard" gem "discard"
# Multi-threaded, Postgres-based, ActiveJob backend for Ruby on Rails
gem "good_job"
group :development, :test do group :development, :test do
# Call 'byebug' anywhere in the code to stop execution and get a debugger console # Call 'byebug' anywhere in the code to stop execution and get a debugger console

19
Gemfile.lock

@ -1,6 +1,6 @@
GIT GIT
remote: https://github.com/rspec/rspec-core.git remote: https://github.com/rspec/rspec-core.git
revision: 053fcfeb6b0b6627edf7261737553a6f7df8cc14 revision: d57c371ee92b16211b80ac7b0b025968438f5297
branch: main branch: main
specs: specs:
rspec-core (3.11.0.pre) rspec-core (3.11.0.pre)
@ -137,14 +137,27 @@ GEM
dotenv (= 2.7.6) dotenv (= 2.7.6)
railties (>= 3.2) railties (>= 3.2)
erubi (1.10.0) erubi (1.10.0)
et-orbi (1.2.5)
tzinfo
factory_bot (6.2.0) factory_bot (6.2.0)
activesupport (>= 5.0.0) activesupport (>= 5.0.0)
factory_bot_rails (6.2.0) factory_bot_rails (6.2.0)
factory_bot (~> 6.2.0) factory_bot (~> 6.2.0)
railties (>= 5.0.0) railties (>= 5.0.0)
ffi (1.15.4) ffi (1.15.4)
fugit (1.5.2)
et-orbi (~> 1.1, >= 1.1.8)
raabro (~> 1.4)
globalid (0.5.2) globalid (0.5.2)
activesupport (>= 5.0) activesupport (>= 5.0)
good_job (2.4.1)
activejob (>= 5.2.0)
activerecord (>= 5.2.0)
concurrent-ruby (>= 1.0.2)
fugit (>= 1.1)
railties (>= 5.2.0)
thor (>= 0.14.1)
zeitwerk (>= 2.0)
govuk-components (2.1.3) govuk-components (2.1.3)
activemodel (>= 6.0) activemodel (>= 6.0)
railties (>= 6.0) railties (>= 6.0)
@ -198,6 +211,7 @@ GEM
public_suffix (4.0.6) public_suffix (4.0.6)
puma (5.5.2) puma (5.5.2)
nio4r (~> 2.0) nio4r (~> 2.0)
raabro (1.4.0)
racc (1.5.2) racc (1.5.2)
rack (2.2.3) rack (2.2.3)
rack-mini-profiler (2.3.3) rack-mini-profiler (2.3.3)
@ -300,7 +314,7 @@ GEM
stimulus-rails (0.7.1) stimulus-rails (0.7.1)
rails (>= 6.0.0) rails (>= 6.0.0)
thor (1.1.0) thor (1.1.0)
turbo-rails (7.1.0) turbo-rails (7.1.1)
rails (>= 6.0.0) rails (>= 6.0.0)
tzinfo (2.0.4) tzinfo (2.0.4)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
@ -337,6 +351,7 @@ DEPENDENCIES
discard discard
dotenv-rails dotenv-rails
factory_bot_rails factory_bot_rails
good_job
govuk-components govuk-components
govuk_design_system_formbuilder govuk_design_system_formbuilder
hotwire-rails hotwire-rails

2
config/application.rb

@ -34,5 +34,7 @@ module DataCollector
# Don't generate system test files. # Don't generate system test files.
config.generators.system_tests = nil config.generators.system_tests = nil
config.active_job.queue_adapter = :good_job
end end
end end

29
db/migrate/20211018125238_create_good_jobs.rb

@ -0,0 +1,29 @@
# frozen_string_literal: true
class CreateGoodJobs < ActiveRecord::Migration[5.2]
def change
enable_extension 'pgcrypto'
create_table :good_jobs, id: :uuid do |t|
t.text :queue_name
t.integer :priority
t.jsonb :serialized_params
t.timestamp :scheduled_at
t.timestamp :performed_at
t.timestamp :finished_at
t.text :error
t.timestamps
t.uuid :active_job_id
t.text :concurrency_key
t.text :cron_key
t.uuid :retried_good_job_id
end
add_index :good_jobs, :scheduled_at, where: "(finished_at IS NULL)", name: "index_good_jobs_on_scheduled_at"
add_index :good_jobs, [:queue_name, :scheduled_at], where: "(finished_at IS NULL)", name: :index_good_jobs_on_queue_name_and_scheduled_at
add_index :good_jobs, [:active_job_id, :created_at], name: :index_good_jobs_on_active_job_id_and_created_at
add_index :good_jobs, :concurrency_key, where: "(finished_at IS NULL)", name: :index_good_jobs_on_concurrency_key_when_unfinished
add_index :good_jobs, [:cron_key, :created_at], name: :index_good_jobs_on_cron_key_and_created_at
end
end

24
db/schema.rb

@ -10,9 +10,10 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2021_10_15_090040) do ActiveRecord::Schema.define(version: 2021_10_18_125238) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "pgcrypto"
enable_extension "plpgsql" enable_extension "plpgsql"
create_table "case_logs", force: :cascade do |t| create_table "case_logs", force: :cascade do |t|
@ -135,4 +136,25 @@ ActiveRecord::Schema.define(version: 2021_10_15_090040) do
t.index ["discarded_at"], name: "index_case_logs_on_discarded_at" t.index ["discarded_at"], name: "index_case_logs_on_discarded_at"
end end
create_table "good_jobs", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.text "queue_name"
t.integer "priority"
t.jsonb "serialized_params"
t.datetime "scheduled_at"
t.datetime "performed_at"
t.datetime "finished_at"
t.text "error"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.uuid "active_job_id"
t.text "concurrency_key"
t.text "cron_key"
t.uuid "retried_good_job_id"
t.index ["active_job_id", "created_at"], name: "index_good_jobs_on_active_job_id_and_created_at"
t.index ["concurrency_key"], name: "index_good_jobs_on_concurrency_key_when_unfinished", where: "(finished_at IS NULL)"
t.index ["cron_key", "created_at"], name: "index_good_jobs_on_cron_key_and_created_at"
t.index ["queue_name", "scheduled_at"], name: "index_good_jobs_on_queue_name_and_scheduled_at", where: "(finished_at IS NULL)"
t.index ["scheduled_at"], name: "index_good_jobs_on_scheduled_at", where: "(finished_at IS NULL)"
end
end end

21
doc/adr/adr-008-queue-job-processing.md

@ -0,0 +1,21 @@
### ADR - 008: Background Job Queueing and Processing
#### Why background jobs?
While we can probably target making validations run fast enough to be able to complete all single case log API actions synchronously in process, with arbitrarily large bulk actions that becomes impossible so we probably need to introduce background job queueing and processing. This will enable us to return an API response immediately and process the Case Logs sent asynchronously.
We will use ActiveJob backed by Good Job (https://github.com/bensheldon/good_job) for this.
#### Why Good Job?
There are multiple options we could use for this, with the main differences being Thread based vs Process based, and Queueing database (Postgres vs Redis):
- Sidekiq (Thread-based, Redis)
- Faktory (Thread-based, Redis)
- Resque (Process-based, Redis)
- Delayed Job (Process-based, Postgres)
- Good Job (Thread-based, Postgres)
Sidekiq is probably the most widely used multi-threaded job backend, and is also widely used in Gov.UK services (https://docs.publishing.service.gov.uk/manual/sidekiq.html) but requires additional infrastructure (Redis). By sticking with a Postgres based backend initially we can keep our architecture simpler, and only add Redis if we need to later.
By using ActiveJob as our interface we can ensure that changing backend later requires minimal if any rewriting of job code. Of the two Postgres based backends Good Job is the more recent, inspired by Delayed Job but specifically written for Rails and ActiveJob making it a good fit here. It also expects to be performant for up to 1-million jobs per day which is more than we're expecting by orders of magnitude.
Loading…
Cancel
Save