sidekiq-ecs-scaler 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 6e788a43a1d4013b88babd60929d35dc465dca7dbe73daa633566929be57f501
4
+ data.tar.gz: 9a6c856115de94d05edb69662d2de03ea0ae536d40d3b36a1f74f1f2f2c9239b
5
+ SHA512:
6
+ metadata.gz: 137a41c9d0c6fbc53bb16652b7d9c711347618e2c3ce37130ce1087ec774c09956c0746e345d6bbd7bca5395aaa36b02c1f1a6a08e9a4c65a3eb51dd347a0280
7
+ data.tar.gz: bdab45f712315863eb32d08e3aba9edb35db1d9d50f2966206baf6948ea1ce97cc9a6672402ffd09cd66ab27d5a222a02fa3fb8d902d9d7e9287bfb4045824e1
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,21 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.6
3
+ NewCops: enable
4
+
5
+ Style/StringLiterals:
6
+ Enabled: true
7
+ EnforcedStyle: double_quotes
8
+
9
+ Style/StringLiteralsInInterpolation:
10
+ Enabled: true
11
+ EnforcedStyle: double_quotes
12
+
13
+ Layout/LineLength:
14
+ Max: 120
15
+
16
+ Metrics/BlockLength:
17
+ Exclude:
18
+ - spec/**/*_spec.rb
19
+
20
+ Naming/FileName:
21
+ Enabled: false
data/.yardopts ADDED
@@ -0,0 +1,3 @@
1
+ --fail-on-warning
2
+ --markup markdown
3
+ --private
data/CHANGELOG.md ADDED
@@ -0,0 +1,4 @@
1
+ ## [0.1.0] - 2021-11-29
2
+
3
+ - initial release
4
+ - only supported when deploying Sidekiq workers to AWS ECS Fargate (1.14.0+)
data/Dockerfile ADDED
@@ -0,0 +1,16 @@
1
+ FROM ruby:3.0.3-slim
2
+
3
+ RUN apt-get update \
4
+ && apt-get install -y build-essential git \
5
+ && apt-get clean \
6
+ && rm -rf /var/lib/apt/lists/*
7
+
8
+ WORKDIR /usr/src/sidekiq-ecs-scaler
9
+
10
+ RUN mkdir -p lib/sidekiq-ecs-scaler \
11
+ && echo "module SidekiqEcsScaler\n VERSION = \"0.1.0\"\nend\n" > lib/sidekiq-ecs-scaler/version.rb
12
+
13
+ COPY bin/setup ./bin/
14
+ COPY Gemfile Gemfile.lock sidekiq-ecs-scaler.gemspec .
15
+
16
+ RUN bin/setup
data/Gemfile ADDED
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in sidekiq-ecs-scaler.gemspec
6
+ gemspec
7
+
8
+ gem "rake"
9
+ gem "rspec"
10
+ gem "rubocop"
11
+ gem "rubocop-rake"
12
+ gem "rubocop-rspec"
13
+ gem "simplecov"
14
+ gem "steep"
15
+ gem "yard"
data/Gemfile.lock ADDED
@@ -0,0 +1,128 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ sidekiq-ecs-scaler (0.1.0)
5
+ aws-sdk-ecs (> 1, < 2)
6
+ sidekiq (> 5, < 7)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ activesupport (6.1.4.1)
12
+ concurrent-ruby (~> 1.0, >= 1.0.2)
13
+ i18n (>= 1.6, < 2)
14
+ minitest (>= 5.1)
15
+ tzinfo (~> 2.0)
16
+ zeitwerk (~> 2.3)
17
+ ast (2.4.2)
18
+ aws-eventstream (1.2.0)
19
+ aws-partitions (1.535.0)
20
+ aws-sdk-core (3.123.0)
21
+ aws-eventstream (~> 1, >= 1.0.2)
22
+ aws-partitions (~> 1, >= 1.525.0)
23
+ aws-sigv4 (~> 1.1)
24
+ jmespath (~> 1.0)
25
+ aws-sdk-ecs (1.91.0)
26
+ aws-sdk-core (~> 3, >= 3.122.0)
27
+ aws-sigv4 (~> 1.1)
28
+ aws-sigv4 (1.4.0)
29
+ aws-eventstream (~> 1, >= 1.0.2)
30
+ concurrent-ruby (1.1.9)
31
+ connection_pool (2.2.5)
32
+ diff-lcs (1.4.4)
33
+ docile (1.4.0)
34
+ ffi (1.15.4)
35
+ i18n (1.8.11)
36
+ concurrent-ruby (~> 1.0)
37
+ jmespath (1.4.0)
38
+ language_server-protocol (3.16.0.3)
39
+ listen (3.7.0)
40
+ rb-fsevent (~> 0.10, >= 0.10.3)
41
+ rb-inotify (~> 0.9, >= 0.9.10)
42
+ minitest (5.14.4)
43
+ parallel (1.21.0)
44
+ parser (3.0.3.0)
45
+ ast (~> 2.4.1)
46
+ rack (2.2.3)
47
+ rainbow (3.0.0)
48
+ rake (13.0.6)
49
+ rb-fsevent (0.11.0)
50
+ rb-inotify (0.10.1)
51
+ ffi (~> 1.0)
52
+ rbs (1.7.1)
53
+ redis (4.5.1)
54
+ regexp_parser (2.1.1)
55
+ rexml (3.2.5)
56
+ rspec (3.10.0)
57
+ rspec-core (~> 3.10.0)
58
+ rspec-expectations (~> 3.10.0)
59
+ rspec-mocks (~> 3.10.0)
60
+ rspec-core (3.10.1)
61
+ rspec-support (~> 3.10.0)
62
+ rspec-expectations (3.10.1)
63
+ diff-lcs (>= 1.2.0, < 2.0)
64
+ rspec-support (~> 3.10.0)
65
+ rspec-mocks (3.10.2)
66
+ diff-lcs (>= 1.2.0, < 2.0)
67
+ rspec-support (~> 3.10.0)
68
+ rspec-support (3.10.3)
69
+ rubocop (1.23.0)
70
+ parallel (~> 1.10)
71
+ parser (>= 3.0.0.0)
72
+ rainbow (>= 2.2.2, < 4.0)
73
+ regexp_parser (>= 1.8, < 3.0)
74
+ rexml
75
+ rubocop-ast (>= 1.12.0, < 2.0)
76
+ ruby-progressbar (~> 1.7)
77
+ unicode-display_width (>= 1.4.0, < 3.0)
78
+ rubocop-ast (1.13.0)
79
+ parser (>= 3.0.1.1)
80
+ rubocop-rake (0.6.0)
81
+ rubocop (~> 1.0)
82
+ rubocop-rspec (2.6.0)
83
+ rubocop (~> 1.19)
84
+ ruby-progressbar (1.11.0)
85
+ sidekiq (6.3.1)
86
+ connection_pool (>= 2.2.2)
87
+ rack (~> 2.0)
88
+ redis (>= 4.2.0)
89
+ simplecov (0.21.2)
90
+ docile (~> 1.1)
91
+ simplecov-html (~> 0.11)
92
+ simplecov_json_formatter (~> 0.1)
93
+ simplecov-html (0.12.3)
94
+ simplecov_json_formatter (0.1.3)
95
+ steep (0.46.0)
96
+ activesupport (>= 5.1)
97
+ language_server-protocol (>= 3.15, < 4.0)
98
+ listen (~> 3.0)
99
+ parallel (>= 1.0.0)
100
+ parser (>= 3.0)
101
+ rainbow (>= 2.2.2, < 4.0)
102
+ rbs (>= 1.2.0)
103
+ terminal-table (>= 2, < 4)
104
+ terminal-table (3.0.2)
105
+ unicode-display_width (>= 1.1.1, < 3)
106
+ tzinfo (2.0.4)
107
+ concurrent-ruby (~> 1.0)
108
+ unicode-display_width (2.1.0)
109
+ yard (0.9.26)
110
+ zeitwerk (2.5.1)
111
+
112
+ PLATFORMS
113
+ x86_64-darwin-19
114
+ x86_64-linux
115
+
116
+ DEPENDENCIES
117
+ rake
118
+ rspec
119
+ rubocop
120
+ rubocop-rake
121
+ rubocop-rspec
122
+ sidekiq-ecs-scaler!
123
+ simplecov
124
+ steep
125
+ yard
126
+
127
+ BUNDLED WITH
128
+ 2.2.32
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2021 shoma07
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,72 @@
1
+ # SidekiqEcsScaler
2
+
3
+ Auto scaler of [Sidekiq](https://github.com/mperham/sidekiq) worker deploymented to AWS ECS.
4
+
5
+ Only supported when deploying Sidekiq workers to AWS ECS Fargate (1.14.0+)
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'sidekiq-ecs-scaler'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle install
18
+
19
+ ## Usage
20
+
21
+ ### Configuration
22
+
23
+ ```ruby
24
+ SidekiqEcsScaler.configure do |config|
25
+ # queue to monitor latency, default is "default"
26
+ config.queue_name = "default"
27
+
28
+ # minimum number of tasks, default is 1
29
+ config.min_count = 1
30
+
31
+ # maximum number of tasks, default is 1
32
+ config.max_count = 3
33
+
34
+ # maximum latency(seconds), default is 3600
35
+ config.max_latency = 3600
36
+
37
+ # custom ECS Client
38
+ config.ecs_client = Aws::ECS::Client.new
39
+
40
+ # Set worker options for scaling
41
+ config.sidekiq_options = { "retry" => true, "queue" => "scheduler" }
42
+ end
43
+ ```
44
+
45
+ ### With Sidekiq Scheduler
46
+
47
+ When using [sidekiq-scheduler](https://github.com/moove-it/sidekiq-scheduler), schedule the scale by setting as follows.
48
+
49
+ ```yml
50
+ # sidekiq.yml
51
+ # example
52
+
53
+ :schedule:
54
+ SidekiqEcsScaler::Worker:
55
+ cron: "0 */15 * * * *"
56
+ # It is safe to set this queue to have a higher priority than the monitored queue.
57
+ queue: scheduler
58
+ ```
59
+
60
+ ## Development
61
+
62
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
63
+
64
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
65
+
66
+ ## Contributing
67
+
68
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/sidekiq-ecs-scaler.
69
+
70
+ ## License
71
+
72
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+
5
+ # rspec
6
+ require "rspec/core/rake_task"
7
+ RSpec::Core::RakeTask.new(:spec)
8
+
9
+ # rubocop
10
+ require "rubocop/rake_task"
11
+ RuboCop::RakeTask.new(:lint) do |t|
12
+ t.options = %w[--parallel]
13
+ end
14
+ namespace :lint do
15
+ desc "Lint fix (Rubocop)"
16
+ task fix: :auto_correct
17
+ end
18
+
19
+ # steep
20
+ # @see https://github.com/soutaro/steep/blob/master/exe/steep
21
+ require "steep"
22
+ require "steep/cli"
23
+ desc "Typecheck (Steep)"
24
+ task :typecheck do
25
+ Steep::CLI.new(
26
+ argv: %w[check -j4], stdout: $stdout, stderr: $stderr, stdin: $stdin
27
+ ).run.zero? || exit(1)
28
+ end
29
+
30
+ # yard
31
+ ## custom rake task
32
+ require "yard"
33
+ desc "document"
34
+ task :doc do
35
+ YARD::CLI::CommandParser.run
36
+ `yard`.lines(chomp: true).last.match(/\d+/)[0].to_i == 100 || exit(1)
37
+ end
38
+
39
+ task default: %i[lint typecheck spec doc]
data/Steepfile ADDED
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ target :lib do
4
+ signature "sig", "sig-private"
5
+
6
+ check "lib"
7
+
8
+ library "json"
9
+ end
data/bin/console ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "sidekiq-ecs-scaler"
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ # require "pry"
12
+ # Pry.start
13
+
14
+ require "irb"
15
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,9 @@
1
+ version: "3"
2
+
3
+ services:
4
+ sidekiq-ecs-scaler:
5
+ build:
6
+ context: .
7
+ volumes:
8
+ - .:/usr/src/sidekiq-ecs-scaler
9
+ command: /bin/bash
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "sidekiq-ecs-scaler"
5
+
6
+ raise "`sidekiq-ecs-scaler` command is not yet supported!"
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SidekiqEcsScaler
4
+ # SidekiqEcsScaler::Client
5
+ class Client
6
+ # @param config [SidekiqEcsScaler::Configuration]
7
+ # @return [void]
8
+ def initialize(config)
9
+ @config = config
10
+ end
11
+
12
+ # @return [Integer, nil]
13
+ def update_desired_count
14
+ return unless config.task_meta
15
+
16
+ desired_count_by_latency.then do |desired_count|
17
+ describe_service.then do |service|
18
+ update_service(service: service, desired_count: desired_count) if service.desired_count != desired_count
19
+
20
+ desired_count
21
+ end
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ # @!attribute [r] config
28
+ # @return [SidekiqEcsScaler::Configuration]
29
+ attr_reader :config
30
+
31
+ # @return [Float]
32
+ def queue_latency
33
+ Sidekiq::Queue.new(config.queue_name).latency
34
+ end
35
+
36
+ # @return [Integer]
37
+ def desired_count_by_latency
38
+ (config.min_count..config.max_count).to_a.at(
39
+ (queue_latency.to_f / config.latency_per_count).floor.to_i
40
+ ) || config.max_count
41
+ end
42
+
43
+ # @return [String]
44
+ def service_name
45
+ config.task_meta!.then do |task_meta|
46
+ config.ecs_client.describe_tasks(
47
+ { cluster: task_meta.cluster, tasks: [task_meta.task_arn] }
48
+ ).tasks.first&.then { |task| task.group.delete_prefix("service:") } || (
49
+ raise Error, "Task(#{task_meta.task_arn}) does not exist in cluster!"
50
+ )
51
+ end
52
+ end
53
+
54
+ # @return [Aws::ECS::Types::Service]
55
+ def describe_service
56
+ config.ecs_client.describe_services(
57
+ { cluster: config.task_meta!.cluster, services: [service_name] }
58
+ ).services.first || (raise Error, "Service(#{service_name}) does not exist in cluster!")
59
+ end
60
+
61
+ # @param service [Aws::ECS::Types::Service]
62
+ # @param desired_count [Integer]
63
+ # @return [Aws::ECS::Types::UpdateServiceResponse]
64
+ def update_service(service:, desired_count:)
65
+ config.ecs_client.update_service(
66
+ {
67
+ cluster: service.cluster_arn,
68
+ service: service.service_name,
69
+ desired_count: desired_count
70
+ }
71
+ )
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SidekiqEcsScaler
4
+ # SidekiqEcsScaler::Configuration
5
+ class Configuration
6
+ # @!attribute [r] queue_name
7
+ # @return [String]
8
+ attr_reader :queue_name
9
+ # @!attribute [r] min_count
10
+ # @return [Integer]
11
+ attr_reader :min_count
12
+ # @!attribute [r] max_count
13
+ # @return [Integer]
14
+ attr_reader :max_count
15
+ # @!attribute [r] max_latency
16
+ # @return [Integer]
17
+ attr_reader :max_latency
18
+ # @!attribute [r] task_meta
19
+ # @return [SidekiqEcsScaler::TaskMetaV4, nil]
20
+ attr_reader :task_meta
21
+ # @!attribute [r] ecs_client
22
+ # @return [Aws::ECS::Client]
23
+ attr_reader :ecs_client
24
+
25
+ # @return [void]
26
+ def initialize
27
+ @queue_name = "default"
28
+ @min_count = 1
29
+ @max_count = 1
30
+ @max_latency = 3600
31
+ @ecs_client = Aws::ECS::Client.new
32
+ @task_meta = TaskMetaV4.build_or_null
33
+ end
34
+
35
+ # @param queue_name [String]
36
+ # @return [void]
37
+ def queue_name=(queue_name)
38
+ raise ArgumentError unless queue_name.instance_of?(String)
39
+
40
+ @queue_name = queue_name
41
+ end
42
+
43
+ # @param min_count [Integer]
44
+ # @return [void]
45
+ # @raise [ArgumentError]
46
+ def min_count=(min_count)
47
+ raise ArgumentError unless min_count.positive?
48
+
49
+ @min_count = min_count
50
+ @max_count = min_count if min_count > max_count
51
+ end
52
+
53
+ # @param max_count [Integer]
54
+ # @return [void]
55
+ # @raise [ArgumentError]
56
+ def max_count=(max_count)
57
+ raise ArgumentError unless max_count.positive?
58
+
59
+ @max_count = max_count
60
+ @min_count = max_count if max_count < min_count
61
+ end
62
+
63
+ # @param max_latency [Integer]
64
+ # @return [void]
65
+ # @raise [ArgumentError]
66
+ def max_latency=(max_latency)
67
+ raise ArgumentError if max_count > max_latency
68
+
69
+ @max_latency = max_latency
70
+ end
71
+
72
+ # @param ecs_client [Aws::ECS::Client]
73
+ # @return [void]
74
+ # @raise [ArgumentError]
75
+ def ecs_client=(ecs_client)
76
+ raise ArgumentError unless ecs_client.is_a?(Aws::ECS::Client)
77
+
78
+ @ecs_client = ecs_client
79
+ end
80
+
81
+ # @return [Integer]
82
+ def latency_per_count
83
+ (max_latency / (1 + max_count - min_count)).tap do |value|
84
+ value.positive? || (raise Error, "latency per count isn't positive!")
85
+ end
86
+ end
87
+
88
+ # @return [SidekiqEcsScaler::TaskMetaV4]
89
+ def task_meta!
90
+ task_meta || (raise Error, "task metadata is null!")
91
+ end
92
+
93
+ # @return [Hash]
94
+ def sidekiq_options
95
+ ::SidekiqEcsScaler::Worker.sidekiq_options
96
+ end
97
+
98
+ # @param sidekiq_options [Hash]
99
+ # @return [void]
100
+ def sidekiq_options=(sidekiq_options)
101
+ raise ArgumentError unless sidekiq_options.is_a?(Hash)
102
+
103
+ ::SidekiqEcsScaler::Worker.sidekiq_options(sidekiq_options)
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SidekiqEcsScaler
4
+ # SidekiqEcsScaler::TaskMetaV4
5
+ #
6
+ # @see https://docs.aws.amazon.com/AmazonECS/latest/userguide/task-metadata-endpoint-v4-fargate.html
7
+ class TaskMetaV4
8
+ class << self
9
+ # @todo If the metadata acquisition fails, an error will be output to the log.
10
+ #
11
+ # @return [SidekiqEcsScaler::TaskMetaV4, nil]
12
+ def build_or_null
13
+ ENV.fetch("ECS_CONTAINER_METADATA_URI_V4", nil)&.then do |uri|
14
+ new(JSON.parse(Net::HTTP.get(URI.parse("#{uri}/task"))))
15
+ end
16
+ rescue StandardError
17
+ nil
18
+ end
19
+ end
20
+
21
+ # @!attribute [r] cluster
22
+ # @return [String]
23
+ attr_reader :cluster
24
+ # @!attribute [r] task_arn
25
+ # @return [String]
26
+ attr_reader :task_arn
27
+
28
+ # @param resp [Hash]
29
+ # @return [void]
30
+ def initialize(resp)
31
+ @cluster = resp.fetch("Cluster")
32
+ @task_arn = resp.fetch("TaskARN")
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SidekiqEcsScaler
4
+ # @return [String]
5
+ VERSION = "0.1.0"
6
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SidekiqEcsScaler
4
+ # SidekiqEcsScaler::Worker
5
+ class Worker
6
+ include Sidekiq::Worker
7
+
8
+ # @return [Integer, nil]
9
+ def perform
10
+ SidekiqEcsScaler.client.update_desired_count
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "uri"
4
+ require "net/http"
5
+ require "json"
6
+ require "sidekiq"
7
+ require "sidekiq/api"
8
+ require "aws-sdk-ecs"
9
+ require_relative "sidekiq-ecs-scaler/version"
10
+ require_relative "sidekiq-ecs-scaler/task_meta_v4"
11
+ require_relative "sidekiq-ecs-scaler/configuration"
12
+ require_relative "sidekiq-ecs-scaler/client"
13
+ require_relative "sidekiq-ecs-scaler/worker"
14
+
15
+ # SidekiqEcsScaler
16
+ module SidekiqEcsScaler
17
+ # SidekiqEcsScaler::Error
18
+ class Error < StandardError; end
19
+
20
+ class << self
21
+ # @return [SidekiqEcsScaler::Configuration]
22
+ def config
23
+ @config ||= Configuration.new
24
+ end
25
+
26
+ # @yieldparam config [SidekiqEcsScaler::Configuration]
27
+ # @yieldreturn [void]
28
+ # @return [void]
29
+ def configure
30
+ raise Error, "No block is given!" unless block_given?
31
+
32
+ yield config
33
+ end
34
+
35
+ # @return [SidekiqEcsScaler::Client]
36
+ def client
37
+ Client.new(config)
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/sidekiq-ecs-scaler/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "sidekiq-ecs-scaler"
7
+ spec.version = SidekiqEcsScaler::VERSION
8
+ spec.authors = ["shoma07"]
9
+ spec.email = ["23730734+shoma07@users.noreply.github.com"]
10
+
11
+ spec.summary = "auto scaler of sidekiq worker deploymented to aws ecs"
12
+ spec.description = "auto scaler of sidekiq worker deploymented to aws ecs"
13
+ spec.homepage = "https://github.com/shoma07/sidekiq-ecs-scaler"
14
+ spec.license = "MIT"
15
+ spec.required_ruby_version = ">= 2.6.0"
16
+
17
+ spec.metadata["homepage_uri"] = spec.homepage
18
+ spec.metadata["source_code_uri"] = spec.homepage
19
+ spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/v#{spec.version}/CHANGELOG.md"
20
+
21
+ # Specify which files should be added to the gem when it is released.
22
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
23
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
24
+ `git ls-files -z`.split("\x0").reject do |f|
25
+ (f == __FILE__) || f.match(%r{\A(?:(?:test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
26
+ end
27
+ end
28
+ spec.bindir = "exe"
29
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
30
+ spec.require_paths = ["lib"]
31
+
32
+ spec.add_dependency "aws-sdk-ecs", "> 1", "< 2"
33
+ spec.add_dependency "sidekiq", "> 5", "< 7"
34
+
35
+ # For more information and examples about making a new gem, checkout our
36
+ # guide at: https://bundler.io/guides/creating_gem.html
37
+ spec.metadata = {
38
+ "rubygems_mfa_required" => "true"
39
+ }
40
+ end
@@ -0,0 +1,82 @@
1
+ module SidekiqEcsScaler
2
+ VERSION: ::String
3
+
4
+ class Error < StandardError
5
+ end
6
+
7
+ def self.config: () -> ::SidekiqEcsScaler::Configuration
8
+
9
+ def self.configure: () { (::SidekiqEcsScaler::Configuration) -> void } -> void
10
+
11
+ def self.client: () -> ::SidekiqEcsScaler::Client
12
+
13
+ class Client
14
+ def initialize: (::SidekiqEcsScaler::Configuration) -> void
15
+
16
+ def update_desired_count: () -> ::Integer?
17
+
18
+ private
19
+
20
+ attr_reader config: ::SidekiqEcsScaler::Configuration
21
+
22
+ def queue_latency: () -> ::Float
23
+
24
+ def desired_count_by_latency: () -> ::Integer
25
+
26
+ def service_name: () -> ::String
27
+
28
+ def describe_service: () -> ::Aws::ECS::Types::Service
29
+
30
+ def update_service: (service: ::Aws::ECS::Types::Service, desired_count: ::Integer) -> ::Aws::ECS::Types::UpdateServiceResponse
31
+ end
32
+
33
+ class Configuration
34
+ attr_accessor queue_name: ::String
35
+
36
+ attr_reader min_count: ::Integer
37
+
38
+ attr_reader max_count: ::Integer
39
+
40
+ attr_reader max_latency: ::Integer
41
+
42
+ attr_reader task_meta: ::SidekiqEcsScaler::TaskMetaV4?
43
+
44
+ attr_reader ecs_client: ::Aws::ECS::Client
45
+
46
+ def initialize: () -> void
47
+
48
+ def min_count=: (::Integer) -> void
49
+
50
+ def max_count=: (::Integer) -> void
51
+
52
+ def max_latency=: (::Integer) -> void
53
+
54
+ def ecs_client=: (::Aws::ECS::Client) -> void
55
+
56
+ def latency_per_count: () -> ::Integer
57
+
58
+ def task_meta!: () -> ::SidekiqEcsScaler::TaskMetaV4
59
+
60
+ def sidekiq_options: () -> ::Hash[untyped, untyped]
61
+
62
+ def sidekiq_options=: (::Hash[untyped, untyped]) -> void
63
+ end
64
+
65
+ class TaskMetaV4
66
+ def self.build_or_null: () -> ::SidekiqEcsScaler::TaskMetaV4?
67
+
68
+ attr_reader cluster: ::String
69
+
70
+ attr_reader task_arn: ::String
71
+
72
+ def initialize: (::Hash[untyped, untyped]) -> void
73
+ end
74
+
75
+ class Worker
76
+ include ::Sidekiq::Worker
77
+
78
+ def self.sidekiq_options: (?::Hash[untyped, untyped]) -> ::Hash[untyped, untyped]
79
+
80
+ def perform: () -> ::Integer?
81
+ end
82
+ end
@@ -0,0 +1,36 @@
1
+ module Aws
2
+ module ECS
3
+ class Client
4
+ def describe_tasks: ({ cluster: ::String, tasks: ::Array[::String] }) -> ::Aws::ECS::Types::DescribeTasksResponse
5
+
6
+ def describe_services: ({ cluster: ::String, services: ::Array[::String] }) -> ::Aws::ECS::Types::DescribeServicesResponse
7
+
8
+ def update_service: ({ cluster: ::String, service: ::String, desired_count: ::Integer }) -> ::Aws::ECS::Types::UpdateServiceResponse
9
+ end
10
+
11
+ module Types
12
+ class DescribeServicesResponse
13
+ def services: () -> ::Array[::Aws::ECS::Types::Service]
14
+ end
15
+
16
+ class DescribeTasksResponse
17
+ def tasks: () -> ::Array[::Aws::ECS::Types::Task]
18
+ end
19
+
20
+ class Service
21
+ def cluster_arn: () -> ::String
22
+
23
+ def service_name: () -> ::String
24
+
25
+ def desired_count: () -> ::Integer
26
+ end
27
+
28
+ class Task
29
+ def group: () -> ::String
30
+ end
31
+
32
+ class UpdateServiceResponse
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,5 @@
1
+ module Net
2
+ class HTTP
3
+ def self.get: (::URI::HTTP) -> ::String
4
+ end
5
+ end
@@ -0,0 +1,11 @@
1
+ module Sidekiq
2
+ module Worker
3
+ def self.sidekiq_options: (?::Hash[untyped, untyped]) -> ::Hash[untyped, untyped]
4
+ end
5
+
6
+ class Queue
7
+ def initialize: (::String) -> void
8
+
9
+ def latency: () -> ::Float
10
+ end
11
+ end
@@ -0,0 +1,6 @@
1
+ module ::URI
2
+ def self.parse: (String) -> ::URI::HTTP
3
+
4
+ class ::URI::HTTP
5
+ end
6
+ end
metadata ADDED
@@ -0,0 +1,112 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sidekiq-ecs-scaler
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - shoma07
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2021-11-29 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: aws-sdk-ecs
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">"
18
+ - !ruby/object:Gem::Version
19
+ version: '1'
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '2'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">"
28
+ - !ruby/object:Gem::Version
29
+ version: '1'
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '2'
33
+ - !ruby/object:Gem::Dependency
34
+ name: sidekiq
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">"
38
+ - !ruby/object:Gem::Version
39
+ version: '5'
40
+ - - "<"
41
+ - !ruby/object:Gem::Version
42
+ version: '7'
43
+ type: :runtime
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">"
48
+ - !ruby/object:Gem::Version
49
+ version: '5'
50
+ - - "<"
51
+ - !ruby/object:Gem::Version
52
+ version: '7'
53
+ description: auto scaler of sidekiq worker deploymented to aws ecs
54
+ email:
55
+ - 23730734+shoma07@users.noreply.github.com
56
+ executables:
57
+ - sidekiq-ecs-scaler
58
+ extensions: []
59
+ extra_rdoc_files: []
60
+ files:
61
+ - ".rspec"
62
+ - ".rubocop.yml"
63
+ - ".yardopts"
64
+ - CHANGELOG.md
65
+ - Dockerfile
66
+ - Gemfile
67
+ - Gemfile.lock
68
+ - LICENSE.txt
69
+ - README.md
70
+ - Rakefile
71
+ - Steepfile
72
+ - bin/console
73
+ - bin/setup
74
+ - docker-compose.yml
75
+ - exe/sidekiq-ecs-scaler
76
+ - lib/sidekiq-ecs-scaler.rb
77
+ - lib/sidekiq-ecs-scaler/client.rb
78
+ - lib/sidekiq-ecs-scaler/configuration.rb
79
+ - lib/sidekiq-ecs-scaler/task_meta_v4.rb
80
+ - lib/sidekiq-ecs-scaler/version.rb
81
+ - lib/sidekiq-ecs-scaler/worker.rb
82
+ - sidekiq-ecs-scaler.gemspec
83
+ - sig-private/aws-sdk-ecs.rbs
84
+ - sig-private/net/http.rbs
85
+ - sig-private/sidekiq.rbs
86
+ - sig-private/uri.rbs
87
+ - sig/sidekiq-ecs-scaler.rbs
88
+ homepage: https://github.com/shoma07/sidekiq-ecs-scaler
89
+ licenses:
90
+ - MIT
91
+ metadata:
92
+ rubygems_mfa_required: 'true'
93
+ post_install_message:
94
+ rdoc_options: []
95
+ require_paths:
96
+ - lib
97
+ required_ruby_version: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: 2.6.0
102
+ required_rubygems_version: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: '0'
107
+ requirements: []
108
+ rubygems_version: 3.2.32
109
+ signing_key:
110
+ specification_version: 4
111
+ summary: auto scaler of sidekiq worker deploymented to aws ecs
112
+ test_files: []