-
Notifications
You must be signed in to change notification settings - Fork 21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Solid Queue Integration #199
Changes from all commits
8f48b11
6ca3cd4
6e64349
620cbaf
c71a1d7
80914e2
186f1f1
e8409bb
5541069
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
name: judoscale-solid_queue tests | ||
defaults: | ||
run: | ||
working-directory: judoscale-solid_queue | ||
on: | ||
push: | ||
branches: | ||
- main | ||
pull_request: | ||
jobs: | ||
test: | ||
strategy: | ||
fail-fast: false | ||
matrix: | ||
gemfile: | ||
- Gemfile | ||
ruby: | ||
- "2.7" | ||
- "3.0" | ||
- "3.1" | ||
- "3.2" | ||
- "3.3" | ||
# exclude: | ||
# - | ||
runs-on: ubuntu-latest | ||
env: # $BUNDLE_GEMFILE must be set at the job level, so it is set for all steps | ||
BUNDLE_GEMFILE: ${{ github.workspace }}/judoscale-solid_queue/${{ matrix.gemfile }} | ||
services: | ||
db: | ||
image: postgres:latest | ||
env: | ||
POSTGRES_HOST_AUTH_METHOD: trust | ||
ports: ["5432:5432"] | ||
options: >- | ||
--health-cmd pg_isready | ||
--health-interval 10s | ||
--health-timeout 5s | ||
--health-retries 5 | ||
steps: | ||
- uses: actions/checkout@v4 | ||
- uses: ruby/setup-ruby@v1 | ||
with: | ||
ruby-version: ${{ matrix.ruby }} | ||
bundler-cache: true # runs bundle install and caches installed gems automatically | ||
- run: bundle exec rake |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,6 +10,7 @@ | |
require "action_controller" | ||
|
||
class TestRailsApp < Rails::Application | ||
config.load_defaults "#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Setting defaults from the current Rails version, eliminates a warning with old cache version format. |
||
config.secret_key_base = "test-secret" | ||
config.eager_load = false | ||
config.logger = ::Logger.new(StringIO.new, progname: "rails-app") | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
source "https://rubygems.org" | ||
|
||
gemspec name: "judoscale-solid_queue" | ||
|
||
gem "judoscale-ruby", path: "../judoscale-ruby" | ||
gem "activerecord" | ||
gem "pg" | ||
gem "minitest" | ||
gem "rake" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
# frozen_string_literal: true | ||
|
||
require "rake/testtask" | ||
|
||
Rake::TestTask.new(:test) do |t| | ||
t.libs << "lib" | ||
t.libs << "test" | ||
t.test_files = FileList["test/**/*_test.rb"] | ||
end | ||
|
||
task default: :test |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
lib = File.expand_path("../lib", __FILE__) | ||
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) | ||
require "judoscale/solid_queue/version" | ||
|
||
Gem::Specification.new do |spec| | ||
spec.name = "judoscale-solid_queue" | ||
spec.version = Judoscale::SolidQueue::VERSION | ||
spec.authors = ["Adam McCrea", "Carlos Antonio da Silva", "Jon Sullivan"] | ||
spec.email = ["[email protected]"] | ||
|
||
spec.summary = "This gem provides SolidQueue integration with the Judoscale autoscaling add-on for Heroku." | ||
spec.homepage = "https://judoscale.com" | ||
spec.license = "MIT" | ||
|
||
spec.metadata = { | ||
"homepage_uri" => "https://judoscale.com", | ||
"bug_tracker_uri" => "https://github.com/judoscale/judoscale-ruby/issues", | ||
"documentation_uri" => "https://judoscale.com/docs", | ||
"changelog_uri" => "https://github.com/judoscale/judoscale-ruby/blob/main/CHANGELOG.md", | ||
"source_code_uri" => "https://github.com/judoscale/judoscale-ruby" | ||
} | ||
|
||
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } | ||
spec.require_paths = ["lib"] | ||
|
||
spec.required_ruby_version = ">= 2.7.0" | ||
|
||
spec.add_dependency "judoscale-ruby", Judoscale::SolidQueue::VERSION | ||
spec.add_dependency "solid_queue", ">= 0.3" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 0.3 is the latest version at the moment, I think it's best to use it as requirement. |
||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# frozen_string_literal: true | ||
|
||
require "judoscale/solid_queue" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
# frozen_string_literal: true | ||
|
||
require "solid_queue" | ||
require "judoscale-ruby" | ||
require "judoscale/config" | ||
require "judoscale/solid_queue/version" | ||
require "judoscale/solid_queue/metrics_collector" | ||
|
||
Judoscale.add_adapter :"judoscale-solid_queue", | ||
{ | ||
adapter_version: Judoscale::SolidQueue::VERSION, | ||
framework_version: ::SolidQueue::VERSION | ||
}, | ||
metrics_collector: Judoscale::SolidQueue::MetricsCollector, | ||
expose_config: Judoscale::Config::JobAdapterConfig.new(:solid_queue) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
# frozen_string_literal: true | ||
|
||
require "judoscale/job_metrics_collector" | ||
require "judoscale/job_metrics_collector/active_record_helper" | ||
require "judoscale/metric" | ||
|
||
module Judoscale | ||
module SolidQueue | ||
class MetricsCollector < Judoscale::JobMetricsCollector | ||
include ActiveRecordHelper | ||
|
||
def self.adapter_config | ||
Judoscale::Config.instance.solid_queue | ||
end | ||
|
||
def self.collect?(config) | ||
super && ActiveRecordHelper.table_exists_for_model?(::SolidQueue::Job) | ||
end | ||
|
||
def initialize | ||
super | ||
|
||
queue_names = run_silently do | ||
::SolidQueue::Job.distinct.pluck(:queue_name) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Querying from the job model will include queues from already finished or error jobs -- basically all known SQ jobs that haven't been deleted yet. (they have documented a way to run a cleanup task that deletes finished jobs after a day, but aren't doing it automatically yet.) |
||
end | ||
self.queues |= queue_names | ||
end | ||
|
||
def collect | ||
metrics = [] | ||
time = Time.now.utc | ||
|
||
oldest_execution_time_by_queue = run_silently do | ||
::SolidQueue::ReadyExecution.group(:queue_name).minimum(:created_at) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Jobs move to the "ready execution" table when they're ready to be picked up by a worker and processed. (so if you enqueue a job right now, it creates both a "job" and a "ready execution" records, but if you schedule one in the future, it creates a "job" and a "scheduled execution" instead -- that laters get moved to "ready execution" when it's time to run it.) |
||
end | ||
self.queues |= oldest_execution_time_by_queue.keys | ||
|
||
if track_busy_jobs? | ||
busy_count_by_queue = run_silently do | ||
::SolidQueue::Job.joins(:claimed_execution).group(:queue_name).count | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Jobs move from "ready execution" to "claimed execution" when they're picked up by a worker. That deletes the "ready" record. Once finished, the "job" is tagged with a "finished_at" value, and "claimed" record is deleted. If failed, a "failed execution" record is also created. |
||
end | ||
self.queues |= busy_count_by_queue.keys | ||
end | ||
|
||
queues.each do |queue| | ||
run_at = oldest_execution_time_by_queue[queue] | ||
latency_ms = run_at ? ((time - run_at) * 1000).ceil : 0 | ||
latency_ms = 0 if latency_ms < 0 | ||
|
||
metrics.push Metric.new(:qt, latency_ms, time, queue) | ||
|
||
if track_busy_jobs? | ||
busy_count = busy_count_by_queue[queue] || 0 | ||
metrics.push Metric.new(:busy, busy_count, Time.now, queue) | ||
end | ||
end | ||
|
||
log_collection(metrics) | ||
metrics | ||
end | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
# frozen_string_literal: true | ||
|
||
module Judoscale | ||
module SolidQueue | ||
VERSION = "1.5.4" | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# frozen_string_literal: true | ||
|
||
require "judoscale/solid_queue" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
lib = File.expand_path("../lib", __FILE__) | ||
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) | ||
require "judoscale/solid_queue/version" | ||
|
||
Gem::Specification.new do |spec| | ||
spec.name = "rails-autoscale-solid_queue" | ||
spec.version = Judoscale::SolidQueue::VERSION | ||
spec.authors = ["Adam McCrea", "Carlos Antonio da Silva", "Jon Sullivan"] | ||
spec.email = ["[email protected]"] | ||
|
||
spec.summary = "This gem provides SolidQueue integration with the Judoscale autoscaling add-on for Heroku." | ||
spec.homepage = "https://judoscale.com" | ||
spec.license = "MIT" | ||
|
||
spec.metadata = { | ||
"homepage_uri" => "https://judoscale.com", | ||
"bug_tracker_uri" => "https://github.com/judoscale/judoscale-ruby/issues", | ||
"documentation_uri" => "https://judoscale.com/docs", | ||
"changelog_uri" => "https://github.com/judoscale/judoscale-ruby/blob/main/CHANGELOG.md", | ||
"source_code_uri" => "https://github.com/judoscale/judoscale-ruby" | ||
} | ||
|
||
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } | ||
spec.require_paths = ["lib"] | ||
|
||
spec.required_ruby_version = ">= 2.7.0" | ||
|
||
spec.add_dependency "rails-autoscale-core", Judoscale::SolidQueue::VERSION | ||
spec.add_dependency "solid_queue", ">= 0.3" | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
# frozen_string_literal: true | ||
|
||
require "test_helper" | ||
require "judoscale/report" | ||
|
||
module Judoscale | ||
describe SolidQueue do | ||
it "adds itself as an adapter with information to be reported to the Judoscale API" do | ||
adapter = Judoscale.adapters.detect { |adapter| adapter.identifier == :"judoscale-solid_queue" } | ||
_(adapter).wont_be_nil | ||
_(adapter.metrics_collector).must_equal Judoscale::SolidQueue::MetricsCollector | ||
|
||
report = ::Judoscale::Report.new(Judoscale.adapters, Judoscale::Config.instance, []) | ||
_(report.as_json[:adapters]).must_include(:"judoscale-solid_queue") | ||
end | ||
|
||
it "sets up a config property for the library" do | ||
config = Config.instance | ||
_(config.solid_queue.enabled).must_equal true | ||
_(config.solid_queue.max_queues).must_equal 20 | ||
_(config.solid_queue.queues).must_equal [] | ||
_(config.solid_queue.track_busy_jobs).must_equal false | ||
|
||
Judoscale.configure do |config| | ||
config.solid_queue.queues = %w[test drive] | ||
config.solid_queue.track_busy_jobs = true | ||
end | ||
|
||
_(config.solid_queue.queues).must_equal %w[test drive] | ||
_(config.solid_queue.track_busy_jobs).must_equal true | ||
|
||
report = ::Judoscale::Report.new(Judoscale.adapters, Judoscale::Config.instance, []) | ||
_(report.as_json[:config]).must_include(:solid_queue) | ||
end | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SolidQueue only works with Rails 7.1+, and so only Ruby 2.7+.