barbeque_client 0.8.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
+ SHA1:
3
+ metadata.gz: 6af93b3528723f585a7877985325b6767ebf6924
4
+ data.tar.gz: cfa14afd2152039fecc9d8c82b60a37b67efca3d
5
+ SHA512:
6
+ metadata.gz: ac6983702e340af395f84936868183abbd8ea0c4b947ee44f04533215a833bafed0038d7bbe582ca8ff5b7ca137b9d773478108655701281ef850a2746411c2d
7
+ data.tar.gz: 407c2073c7d61819028ffd0a1db5ce20a59c2b613e65a8bcc6a4076d35f0f3fc93ce91bd34fae65c711a65c278ad7c54f23a679162bbc3208e682787f4099be0
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.3
4
+ before_install: gem install bundler -v 1.11.2
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in barbeque.gemspec
4
+ gemspec
5
+
6
+ gem 'pry-byebug'
7
+ gem 'pry-doc'
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Takashi Kokubun
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,41 @@
1
+ # BarbequeClient
2
+
3
+ BarbequeClient client for Ruby.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```rb
10
+ gem 'barbeque_client'
11
+ ```
12
+
13
+ And create "config/initializers/barbeque.rb" and edit it like:
14
+
15
+ ```rb
16
+ BarbequeClient.configure do |config|
17
+ config.application = 'cookpad'
18
+ config.default_queue = 'default'
19
+ config.endpoint = 'https://barbeque.example.com'
20
+ end
21
+ ```
22
+
23
+ ## Usage
24
+ ### Enqueuing a job
25
+
26
+ ```rb
27
+ execution = BarbequeClient.enqueue(
28
+ job: 'NotifyAuthor', # @param [String] job - Job name to enqueue.
29
+ message: { user_id: 7553989 }, # @param [Object] message - An object which is serializable as JSON.
30
+ queue: 'default', # @param optional [String] queue - A queue name to enqueue a job.
31
+ )
32
+ execution.message_id #=> "a3c653c1-335e-4d4d-a6f9-eb91c0253d02"
33
+ execution.status #=> "pending"
34
+ ```
35
+
36
+ ### Polling the job's status
37
+
38
+ ```rb
39
+ message_id = "a3c653c1-335e-4d4d-a6f9-eb91c0253d02"
40
+ BarbequeClient.status(message_id: message_id) #=> "success"
41
+ ```
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task default: :spec
data/barbeque.gemspec ADDED
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'barbeque_client/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'barbeque_client'
8
+ spec.version = BarbequeClient::VERSION
9
+ spec.authors = ['Takashi Kokubun']
10
+ spec.email = ['takashi-kokubun@cookpad.com']
11
+
12
+ spec.summary = %q{Barbeque client for Ruby}
13
+ spec.description = %q{Barbeque client for Ruby}
14
+ spec.homepage = 'https://github.com/cookpad/barbeque_client'
15
+ spec.license = 'MIT'
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ spec.bindir = 'exe'
19
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
+ spec.require_paths = ['lib']
21
+
22
+ spec.add_dependency 'garage_client'
23
+ spec.add_development_dependency 'bundler', '~> 1.11'
24
+ spec.add_development_dependency 'pry'
25
+ spec.add_development_dependency 'rails', '~> 4.2'
26
+ spec.add_development_dependency 'rake', '~> 10.0'
27
+ spec.add_development_dependency 'rspec', '~> 3.0'
28
+ spec.add_development_dependency 'rspec-rails'
29
+ end
data/bin/console ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "barbeque"
5
+
6
+ require "pry"
7
+ Pry.start
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,28 @@
1
+ module ActiveJob
2
+ module QueueAdapters
3
+ class BarbequeAdapter
4
+ # Interface for ActiveJob 5.0
5
+ def enqueue(job)
6
+ BarbequeAdapter.enqueue(job)
7
+ end
8
+
9
+ class << self
10
+ # Interface for ActiveJob 4.2
11
+ def enqueue(job)
12
+ execution = BarbequeClient.enqueue(
13
+ job: job.class.to_s,
14
+ message: ActiveJob::Arguments.serialize(job.arguments),
15
+ queue: job.queue_name,
16
+ )
17
+ job.job_id = execution.message_id
18
+ end
19
+
20
+ def enqueue_at(job, timestamp)
21
+ raise NotImplementedError.new(
22
+ 'Currently setting timestamp is not supported'
23
+ )
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,51 @@
1
+ require 'barbeque_client/configuration'
2
+ require 'barbeque_client/client'
3
+ require 'barbeque_client/version'
4
+ require 'barbeque_client/executor'
5
+
6
+ begin
7
+ require 'rails'
8
+ rescue LoadError
9
+ else
10
+ require 'barbeque_client/railtie'
11
+ end
12
+
13
+ module BarbequeClient
14
+ class << self
15
+ def configure
16
+ yield config
17
+ end
18
+
19
+ def config
20
+ @config ||= Configuration.new
21
+ end
22
+
23
+ # @param [String] job - Job name to enqueue.
24
+ # @param [Object] message - An object which is serializable as JSON.
25
+ # @param optional [String] queue - A queue name to enqueue a job.
26
+ # @return [Hashie::Mash] resonse - { message_id: String, status: String }
27
+ def enqueue(job:, message:, queue: nil)
28
+ response = client.create_execution(
29
+ job: job,
30
+ message: message,
31
+ queue: queue,
32
+ )
33
+ response.body
34
+ end
35
+
36
+ # @param [String] message_id - Job execution's message_id to check status
37
+ # @return [String] status - Job execution's status like "success", "pending", "failure", ...
38
+ def status(message_id:)
39
+ response = client.execution(message_id: message_id)
40
+ response.body.status
41
+ end
42
+
43
+ def client
44
+ @client ||= Client.new(
45
+ application: config.application,
46
+ default_queue: config.default_queue,
47
+ endpoint: config.endpoint,
48
+ )
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,54 @@
1
+ require 'garage_client'
2
+ require 'json'
3
+
4
+ module BarbequeClient
5
+ class Client
6
+ def initialize(application:, default_queue:, endpoint:)
7
+ @application = application
8
+ @default_queue = default_queue
9
+ @endpoint = endpoint
10
+ end
11
+
12
+ # @param [String] job - Job name to enqueue.
13
+ # @param [Object] message - An object which is serializable as JSON.
14
+ # @param optional [String] queue - A queue name to enqueue a job.
15
+ # @return [Faraday::Response]
16
+ def create_execution(job:, message:, queue: nil)
17
+ params = {
18
+ application: @application,
19
+ job: job,
20
+ message: message,
21
+ queue: queue || @default_queue,
22
+ }
23
+ result = garage_client.post('/v2/job_executions', params)
24
+ result.response
25
+ end
26
+
27
+ # @param [String] message_id - Job execution's message_id to retry
28
+ # @param [Integer] delay_seconds - Retry delay in seconds. Maximum is 900s.
29
+ # @return [Faraday::Response]
30
+ def retry_execution(message_id:, delay_seconds: 0)
31
+ result = garage_client.post(
32
+ "/v1/job_executions/#{message_id}/retries",
33
+ delay_seconds: delay_seconds,
34
+ )
35
+ result.response
36
+ end
37
+
38
+ # @param [String] message_id - Job execution's message_id to check status
39
+ # @return [Faraday::Response]
40
+ def execution(message_id:)
41
+ result = garage_client.get("/v1/job_executions/#{message_id}")
42
+ result.response
43
+ end
44
+
45
+ private
46
+
47
+ def garage_client
48
+ @garage_client ||= GarageClient::Client.new(
49
+ endpoint: @endpoint,
50
+ path_prefix: '/',
51
+ )
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,13 @@
1
+ module BarbequeClient
2
+ class Configuration
3
+ def initialize
4
+ @default_queue = 'default'
5
+ end
6
+
7
+ attr_accessor *%i[
8
+ application
9
+ default_queue
10
+ endpoint
11
+ ]
12
+ end
13
+ end
@@ -0,0 +1,25 @@
1
+ require 'json'
2
+
3
+ module BarbequeClient
4
+ class Executor
5
+ # @param [String] job - Job class name
6
+ # @param [String] message - JSON-serialized object
7
+ # @param [String] message_id - SQS mesasge_id
8
+ # @param [String] queue_name - barbeque's job_queues.name
9
+ def initialize(job:, message:, message_id:, queue_name:)
10
+ @job = job
11
+ @message = JSON.load(message)
12
+ @message_id = message_id
13
+ @queue_name = queue_name
14
+ end
15
+
16
+ def run
17
+ ActiveJob::Base.execute(
18
+ 'job_class' => @job,
19
+ 'job_id' => @message_id,
20
+ 'queue_name' => @queue_name,
21
+ 'arguments' => @message,
22
+ )
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,30 @@
1
+ module BarbequeClient
2
+ class ExponentialRetry
3
+ MAX_DELAY_SECONDS = (ENV['BARBEQUE_MAX_RETRY_DELAY'] || 900).to_i
4
+
5
+ # https://github.com/mperham/sidekiq/blob/v4.1.2/lib/sidekiq/middleware/server/retry_jobs.rb#L176-L179
6
+ # @return [Integer] seconds
7
+ def self.exponential_backoff(count)
8
+ (count ** 4) + 15 + (rand(30) * (count + 1))
9
+ end
10
+
11
+ # @param [Integer] count - Count of retry
12
+ def initialize(count)
13
+ @count = count
14
+ end
15
+
16
+ # @param [String] message_id
17
+ def retry(message_id)
18
+ BarbequeClient.client.retry_execution(
19
+ message_id: message_id,
20
+ delay_seconds: [delay_seconds, MAX_DELAY_SECONDS].min,
21
+ )
22
+ end
23
+
24
+ private
25
+
26
+ def delay_seconds
27
+ ExponentialRetry.exponential_backoff(@count)
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,12 @@
1
+ ActiveSupport.on_load(:active_job) do
2
+ require 'active_job/queue_adapters/barbeque_adapter'
3
+ require 'barbeque_client/retryable'
4
+ end
5
+
6
+ module BarbequeClient
7
+ class Railtie < Rails::Railtie
8
+ rake_tasks do
9
+ load 'barbeque_client/tasks/execute.rake'
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,28 @@
1
+ require 'barbeque_client/exponential_retry'
2
+
3
+ module BarbequeClient
4
+ module Retryable
5
+ extend ActiveSupport::Concern
6
+
7
+ class EmptyRetryCount < StandardError; end
8
+
9
+ module ClassMethods
10
+ def barbeque_retry(limit:, retryable_exceptions: nil)
11
+ exceptions = Array.wrap(retryable_exceptions || StandardError)
12
+
13
+ rescue_from *exceptions do |exception|
14
+ unless ENV['BARBEQUE_RETRY_COUNT']
15
+ raise EmptyRetryCount.new('ENV["BARBEQUE_RETRY_COUNT"] is not set')
16
+ end
17
+ count = ENV['BARBEQUE_RETRY_COUNT'].to_i
18
+
19
+ if count < limit
20
+ ExponentialRetry.new(count).retry(self.job_id)
21
+ else
22
+ raise exception
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,42 @@
1
+ require 'sinatra/base'
2
+ require 'securerandom'
3
+ require 'json'
4
+
5
+ module BarbequeClient
6
+ # Fake API to run job locally
7
+ class Runner < Sinatra::Base
8
+ set :port, ENV['PORT'] || 3003
9
+
10
+ post '/v1/job_executions' do
11
+ params = JSON.parse(request.body.read)
12
+ spawn(
13
+ 'bundle', 'exec', 'rake', 'barbeque:execute',
14
+ "BARBEQUE_JOB=#{params['job']}", "BARBEQUE_MESSAGE=#{params['message']}",
15
+ "BARBEQUE_RETRY_COUNT=0",
16
+ )
17
+
18
+ content_type :json
19
+ { status: 'pending', message_id: SecureRandom.uuid }.to_json
20
+ end
21
+
22
+ post '/v2/job_executions' do
23
+ params = JSON.parse(request.body.read)
24
+ spawn(
25
+ 'bundle', 'exec', 'rake', 'barbeque:execute',
26
+ "BARBEQUE_JOB=#{params['job']}", "BARBEQUE_MESSAGE=#{params['message'].to_json}",
27
+ "BARBEQUE_RETRY_COUNT=0",
28
+ )
29
+
30
+ content_type :json
31
+ { status: 'pending', message_id: SecureRandom.uuid }.to_json
32
+ end
33
+
34
+ post '/v1/job_executions/:message_id/retries' do
35
+ # TODO: Save message on "/v1/job_executions" and stop skipping retry.
36
+ puts "Received retry: #{params['message_id']} (retry skipped)"
37
+
38
+ content_type :json
39
+ { status: 'pending', message_id: SecureRandom.uuid }.to_json
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,17 @@
1
+ namespace :barbeque do
2
+ desc 'Execute ActiveJob task with barbeque envs'
3
+ task execute: :environment do
4
+ BarbequeClient::Executor.new(
5
+ job: ENV['BARBEQUE_JOB'],
6
+ message: ENV['BARBEQUE_MESSAGE'],
7
+ message_id: ENV['BARBEQUE_MESSAGE_ID'],
8
+ queue_name: ENV['BARBEQUE_QUEUE_NAME'],
9
+ ).run
10
+ end
11
+
12
+ desc 'Start a fake barbeque API for development'
13
+ task :runner do
14
+ require 'barbeque_client/runner'
15
+ BarbequeClient::Runner.run!
16
+ end
17
+ end
@@ -0,0 +1,3 @@
1
+ module BarbequeClient
2
+ VERSION = '0.8.0'
3
+ end
metadata ADDED
@@ -0,0 +1,163 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: barbeque_client
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.8.0
5
+ platform: ruby
6
+ authors:
7
+ - Takashi Kokubun
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-09-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: garage_client
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.11'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.11'
41
+ - !ruby/object:Gem::Dependency
42
+ name: pry
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rails
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '4.2'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '4.2'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '10.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '10.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '3.0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '3.0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rspec-rails
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ description: Barbeque client for Ruby
112
+ email:
113
+ - takashi-kokubun@cookpad.com
114
+ executables: []
115
+ extensions: []
116
+ extra_rdoc_files: []
117
+ files:
118
+ - ".gitignore"
119
+ - ".rspec"
120
+ - ".travis.yml"
121
+ - Gemfile
122
+ - LICENSE.txt
123
+ - README.md
124
+ - Rakefile
125
+ - barbeque.gemspec
126
+ - bin/console
127
+ - bin/setup
128
+ - lib/active_job/queue_adapters/barbeque_adapter.rb
129
+ - lib/barbeque_client.rb
130
+ - lib/barbeque_client/client.rb
131
+ - lib/barbeque_client/configuration.rb
132
+ - lib/barbeque_client/executor.rb
133
+ - lib/barbeque_client/exponential_retry.rb
134
+ - lib/barbeque_client/railtie.rb
135
+ - lib/barbeque_client/retryable.rb
136
+ - lib/barbeque_client/runner.rb
137
+ - lib/barbeque_client/tasks/execute.rake
138
+ - lib/barbeque_client/version.rb
139
+ homepage: https://github.com/cookpad/barbeque_client
140
+ licenses:
141
+ - MIT
142
+ metadata: {}
143
+ post_install_message:
144
+ rdoc_options: []
145
+ require_paths:
146
+ - lib
147
+ required_ruby_version: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - ">="
150
+ - !ruby/object:Gem::Version
151
+ version: '0'
152
+ required_rubygems_version: !ruby/object:Gem::Requirement
153
+ requirements:
154
+ - - ">="
155
+ - !ruby/object:Gem::Version
156
+ version: '0'
157
+ requirements: []
158
+ rubyforge_project:
159
+ rubygems_version: 2.5.1
160
+ signing_key:
161
+ specification_version: 4
162
+ summary: Barbeque client for Ruby
163
+ test_files: []