expeditor 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
+ SHA1:
3
+ metadata.gz: 28e3d0c9d21f60030c7e591f6fd79c849357b428
4
+ data.tar.gz: e3fd9e317a4d5fcd3b27bb4f9d4b8c48a03dc436
5
+ SHA512:
6
+ metadata.gz: 51d080f2f60dde13877de518bdf1ad4893cc98bfa970e3e8ee9891e754445f3564c517beadcec923b62900c44ef97bb0906dcead8dd78d4f7c92f823f186f6c9
7
+ data.tar.gz: 95753cfcd208ebe0e29ed06a51c69bbf4589d3252e299748a83f13ed17aa326e3a1e68483bbf9b7211a50985309c30b8c8db9de4d7e79b351005495822351fb2
data/.gitignore ADDED
@@ -0,0 +1,10 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ /vendor/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.1
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rystrix.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,37 @@
1
+ # Expeditor
2
+
3
+ Expeditor is a Ruby library inspired by Netflix/Hystrix.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'expeditor'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install expeditor
20
+
21
+ ## Usage
22
+
23
+ TODO: Write usage instructions here
24
+
25
+ ## Development
26
+
27
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/console` for an interactive prompt that will allow you to experiment.
28
+
29
+ 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` to create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
30
+
31
+ ## Contributing
32
+
33
+ 1. Fork it ( https://github.com/[my-github-username]/expeditor/fork )
34
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
35
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
36
+ 4. Push to the branch (`git push origin my-new-feature`)
37
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,5 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+ task default: :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "rystrix"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,62 @@
1
+ require 'expeditor'
2
+
3
+ start_time = Time.now
4
+
5
+ # Create new service (it is containing a thread pool and circuit breaker function)
6
+ service = Expeditor::Service.new(
7
+ executor: Concurrent::ThreadPoolExecutor.new(
8
+ min_threads: 5, # minimum number of threads
9
+ max_threads: 5, # maximum number of threads
10
+ max_queue: 0, # max size of task queue (including executing threads)
11
+ ),
12
+ non_break_count: 10, # max count of non break
13
+ threshold: 0.5, # failure rate to break (0.0 - 1.0)
14
+ )
15
+
16
+ # Create commands
17
+ command1 = Expeditor::Command.new(service: service) do
18
+ sleep 0.1
19
+ 'command1'
20
+ end
21
+
22
+ command2 = Expeditor::Command.new(service: service, timeout: 0.5) do
23
+ sleep 1000
24
+ 'command2'
25
+ end
26
+ # command2_d is command2 with fallback
27
+ command2_d = command2.with_fallback do |e|
28
+ 'command2 fallback'
29
+ end
30
+
31
+ command3 = Expeditor::Command.new(
32
+ service: service,
33
+ dependencies: [command1, command2_d]
34
+ ) do |v1, v2|
35
+ sleep 0.2
36
+ v1 + ', ' + v2
37
+ end
38
+
39
+ command4 = Expeditor::Command.new(
40
+ service: service,
41
+ dependencies: [command2, command3],
42
+ timeout: 1
43
+ ) do |v2, v3|
44
+ sleep 0.3
45
+ v2 + ', ' + v3
46
+ end
47
+ command4_d = command4.with_fallback do
48
+ 'command4 fallback'
49
+ end
50
+
51
+ # Start command (all dependencies of command4_d are executed. this is non blocking)
52
+ command4_d.start
53
+
54
+ puts Time.now - start_time #=> 0.00...
55
+ puts command1.get #=> command1
56
+ puts Time.now - start_time #=> 0.10...
57
+ puts command2_d.get #=> command2 fallback
58
+ puts Time.now - start_time #=> 0.50...
59
+ puts command4_d.get #=> command4 fallback
60
+ puts Time.now - start_time #=> 0.50...
61
+ puts command3.get #=> command3
62
+ puts Time.now - start_time #=> 0.70...
data/expeditor.gemspec ADDED
@@ -0,0 +1,32 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'expeditor/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "expeditor"
8
+ spec.version = Expeditor::VERSION
9
+ spec.authors = ["shohei-yasutake"]
10
+ spec.email = ["shohei-yasutake@cookpad.com"]
11
+
12
+ spec.summary = "Expeditor provides asynchronous execution and fault tolerance for microservices"
13
+ spec.description = "Expeditor provides asynchronous execution and fault tolerance for microservices"
14
+ spec.homepage = "https://github.com/cookpad/expeditor"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.bindir = "exe"
18
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
+ spec.require_paths = ["lib"]
20
+
21
+ if spec.respond_to?(:metadata)
22
+ spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com' to prevent pushes to rubygems.org, or delete to allow pushes to any server."
23
+ end
24
+
25
+ spec.add_runtime_dependency "concurrent-ruby", "~> 0.8"
26
+ spec.add_runtime_dependency "concurrent-ruby-ext", "~> 0.8"
27
+ spec.add_runtime_dependency "retryable", "> 1.0"
28
+
29
+ spec.add_development_dependency "bundler", "~> 1.9"
30
+ spec.add_development_dependency "rake", "~> 10.0"
31
+ spec.add_development_dependency "rspec", "3.2.0"
32
+ end
data/lib/expeditor.rb ADDED
@@ -0,0 +1,7 @@
1
+ require 'expeditor/bucket'
2
+ require 'expeditor/command'
3
+ require 'expeditor/errors'
4
+ require 'expeditor/rich_future'
5
+ require 'expeditor/service'
6
+ require 'expeditor/status'
7
+ require 'expeditor/version'
@@ -0,0 +1,79 @@
1
+ require 'expeditor/status'
2
+
3
+ module Expeditor
4
+ class Bucket
5
+ def initialize(opts = {})
6
+ @mutex = Mutex.new
7
+ @current_index = 0
8
+ @size = opts.fetch(:size, 10)
9
+ @per_time = opts.fetch(:per, 1)
10
+ @current_start = Time.now
11
+ @statuses = [].fill(0..(@size - 1)) do
12
+ Expeditor::Status.new
13
+ end
14
+ end
15
+
16
+ def increment(type)
17
+ @mutex.synchronize do
18
+ update
19
+ @statuses[@current_index].increment type
20
+ end
21
+ end
22
+
23
+ def total
24
+ acc = @mutex.synchronize do
25
+ update
26
+ @statuses.inject([0, 0, 0, 0, 0, 0]) do |acc, s|
27
+ acc[0] += s.success
28
+ acc[1] += s.failure
29
+ acc[2] += s.rejection
30
+ acc[3] += s.timeout
31
+ acc[4] += s.break
32
+ acc[5] += s.dependency
33
+ acc
34
+ end
35
+ end
36
+ status = Expeditor::Status.new
37
+ status.success = acc[0]
38
+ status.failure = acc[1]
39
+ status.rejection = acc[2]
40
+ status.timeout = acc[3]
41
+ status.break = acc[4]
42
+ status.dependency = acc[5]
43
+ status
44
+ end
45
+
46
+ def current
47
+ @mutex.synchronize do
48
+ update
49
+ @statuses[@current_index]
50
+ end
51
+ end
52
+
53
+ private
54
+
55
+ def update
56
+ passing = last_passing
57
+ if passing > 0
58
+ @current_start = @current_start + @per_time * passing
59
+ passing = passing.div @size + @size if passing > 2 * @size
60
+ passing.times do
61
+ @current_index = next_index
62
+ @statuses[@current_index].reset
63
+ end
64
+ end
65
+ end
66
+
67
+ def last_passing
68
+ (Time.now - @current_start).div @per_time
69
+ end
70
+
71
+ def next_index
72
+ if @current_index == @size - 1
73
+ 0
74
+ else
75
+ @current_index + 1
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,242 @@
1
+ require 'concurrent/ivar'
2
+ require 'concurrent/executor/safe_task_executor'
3
+ require 'concurrent/configuration'
4
+ require 'expeditor/errors'
5
+ require 'expeditor/rich_future'
6
+ require 'expeditor/service'
7
+ require 'expeditor/services'
8
+ require 'retryable'
9
+ require 'timeout'
10
+
11
+ module Expeditor
12
+ class Command
13
+ def initialize(opts = {}, &block)
14
+ @service = opts.fetch(:service, Expeditor::Services.default)
15
+ @timeout = opts[:timeout]
16
+ @dependencies = opts.fetch(:dependencies, [])
17
+ @normal_future = initial_normal(&block)
18
+ @fallback_var = nil
19
+ @retryable_options = Concurrent::IVar.new
20
+ end
21
+
22
+ def start
23
+ if not started?
24
+ @dependencies.each(&:start)
25
+ @normal_future.safe_execute
26
+ end
27
+ self
28
+ end
29
+
30
+ # Equivalent to retryable gem options
31
+ def start_with_retry(retryable_options = {})
32
+ if not started?
33
+ @retryable_options.set(retryable_options)
34
+ start
35
+ end
36
+ self
37
+ end
38
+
39
+ def started?
40
+ @normal_future.executed?
41
+ end
42
+
43
+ def get
44
+ raise NotStartedError if not started?
45
+ @normal_future.get_or_else do
46
+ if @fallback_var
47
+ @fallback_var.wait
48
+ if @fallback_var.rejected?
49
+ raise @fallback_var.reason
50
+ else
51
+ @fallback_var.value
52
+ end
53
+ else
54
+ raise @normal_future.reason
55
+ end
56
+ end
57
+ end
58
+
59
+ def with_fallback(&block)
60
+ command = self.clone
61
+ command.reset_fallback(&block)
62
+ command
63
+ end
64
+
65
+ def wait
66
+ raise NotStartedError if not started?
67
+ @normal_future.wait
68
+ @fallback_var.wait if @fallback_var
69
+ end
70
+
71
+ # command.on_complete do |success, value, reason|
72
+ # ...
73
+ # end
74
+ def on_complete(&block)
75
+ on do |_, value, reason|
76
+ block.call(reason == nil, value, reason)
77
+ end
78
+ end
79
+
80
+ # command.on_success do |value|
81
+ # ...
82
+ # end
83
+ def on_success(&block)
84
+ on do |_, value, reason|
85
+ block.call(value) unless reason
86
+ end
87
+ end
88
+
89
+ # command.on_failure do |e|
90
+ # ...
91
+ # end
92
+ def on_failure(&block)
93
+ on do |_, _, reason|
94
+ block.call(reason) if reason
95
+ end
96
+ end
97
+
98
+ # `chain` returns new command that has self as dependencies
99
+ def chain(opts = {}, &block)
100
+ opts[:dependencies] = [self]
101
+ Command.new(opts, &block)
102
+ end
103
+
104
+ def self.const(value)
105
+ ConstCommand.new(value)
106
+ end
107
+
108
+ def self.start(opts = {}, &block)
109
+ Command.new(opts, &block).start
110
+ end
111
+
112
+ protected
113
+
114
+ def reset_fallback(&block)
115
+ @fallback_var = Concurrent::IVar.new
116
+ @normal_future.add_observer do |_, value, reason|
117
+ if reason != nil
118
+ future = RichFuture.new(executor: Concurrent.configuration.global_task_pool) do
119
+ success, val, reason = Concurrent::SafeTaskExecutor.new(block, rescue_exception: true).execute(reason)
120
+ @fallback_var.complete(success, val, reason)
121
+ end
122
+ future.safe_execute
123
+ else
124
+ @fallback_var.complete(true, value, nil)
125
+ end
126
+ end
127
+ end
128
+
129
+ private
130
+
131
+ def breakable_block(args, &block)
132
+ if @service.open?
133
+ raise CircuitBreakError
134
+ else
135
+ block.call(*args)
136
+ end
137
+ end
138
+
139
+ def retryable_block(args, &block)
140
+ if @retryable_options.fulfilled?
141
+ Retryable.retryable(@retryable_options.value) do |retries, exception|
142
+ metrics(exception) if retries > 0
143
+ breakable_block(args, &block)
144
+ end
145
+ else
146
+ breakable_block(args, &block)
147
+ end
148
+ end
149
+
150
+ def timeout_block(args, &block)
151
+ if @timeout
152
+ Timeout::timeout(@timeout) do
153
+ retryable_block(args, &block)
154
+ end
155
+ else
156
+ retryable_block(args, &block)
157
+ end
158
+ end
159
+
160
+ def metrics(reason)
161
+ case reason
162
+ when nil
163
+ @service.success
164
+ when Timeout::Error
165
+ @service.timeout
166
+ when RejectedExecutionError
167
+ @service.rejection
168
+ when CircuitBreakError
169
+ @service.break
170
+ when DependencyError
171
+ @service.dependency
172
+ else
173
+ @service.failure
174
+ end
175
+ end
176
+
177
+ # timeout do
178
+ # retryable do
179
+ # circuit break do
180
+ # block.call
181
+ # end
182
+ # end
183
+ # end
184
+ def initial_normal(&block)
185
+ future = RichFuture.new(executor: @service.executor) do
186
+ args = wait_dependencies
187
+ timeout_block(args, &block)
188
+ end
189
+ future.add_observer do |_, _, reason|
190
+ metrics(reason)
191
+ end
192
+ future
193
+ end
194
+
195
+ def wait_dependencies
196
+ if @dependencies.count > 0
197
+ current = Thread.current
198
+ executor = Concurrent::ThreadPoolExecutor.new(
199
+ min_threads: 0,
200
+ max_threads: 5,
201
+ max_queue: 0,
202
+ )
203
+ error = Concurrent::IVar.new
204
+ error.add_observer do |_, e, _|
205
+ executor.shutdown
206
+ current.raise(DependencyError.new(e))
207
+ end
208
+ args = []
209
+ @dependencies.each_with_index do |c, i|
210
+ executor.post do
211
+ begin
212
+ args[i] = c.get
213
+ rescue => e
214
+ error.set(e)
215
+ end
216
+ end
217
+ end
218
+ executor.shutdown
219
+ executor.wait_for_termination
220
+ args
221
+ else
222
+ []
223
+ end
224
+ end
225
+
226
+ def on(&callback)
227
+ if @fallback_var
228
+ @fallback_var.add_observer(&callback)
229
+ else
230
+ @normal_future.add_observer(&callback)
231
+ end
232
+ end
233
+
234
+ class ConstCommand < Command
235
+ def initialize(value)
236
+ @service = Expeditor::Services.default
237
+ @dependencies = []
238
+ @normal_future = RichFuture.new {}.set(value)
239
+ end
240
+ end
241
+ end
242
+ end
@@ -0,0 +1,13 @@
1
+ require 'concurrent/errors'
2
+
3
+ module Expeditor
4
+ NotStartedError = Class.new(StandardError)
5
+ RejectedExecutionError = Concurrent::RejectedExecutionError
6
+ CircuitBreakError = Class.new(StandardError)
7
+ class DependencyError < StandardError
8
+ attr :error
9
+ def initialize(e)
10
+ @error = e
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,61 @@
1
+ require 'concurrent/configuration'
2
+ require 'concurrent/future'
3
+
4
+ module Expeditor
5
+ class RichFuture < Concurrent::Future
6
+ def get
7
+ wait
8
+ if rejected?
9
+ raise reason
10
+ else
11
+ value
12
+ end
13
+ end
14
+
15
+ def get_or_else(&block)
16
+ wait
17
+ if rejected?
18
+ block.call
19
+ else
20
+ value
21
+ end
22
+ end
23
+
24
+ def set(v)
25
+ super(v)
26
+ end
27
+
28
+ def safe_set(v)
29
+ set(v) unless completed?
30
+ end
31
+
32
+ def fail(e)
33
+ super(e)
34
+ end
35
+
36
+ def safe_fail(e)
37
+ fail(e) unless completed?
38
+ end
39
+
40
+ def executed?
41
+ not unscheduled?
42
+ end
43
+
44
+ def safe_execute
45
+ begin
46
+ execute
47
+ rescue Exception => e
48
+ fail(e)
49
+ end
50
+ end
51
+
52
+ private
53
+
54
+ # This is workaround for concurrent-ruby's deadlock bug
55
+ # see: ruby-concurrency/concurrent-ruby#275
56
+ def work
57
+ success, val, reason = Concurrent::SafeTaskExecutor.new(@task, rescue_exception: true).execute(*@args)
58
+ complete(success, val, reason)
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,82 @@
1
+ require 'concurrent/executor/thread_pool_executor'
2
+
3
+ module Expeditor
4
+ class Service
5
+ attr_reader :executor
6
+
7
+ def initialize(opts = {})
8
+ @executor = opts.fetch(:executor) { Concurrent::ThreadPoolExecutor.new }
9
+ @threshold = opts.fetch(:threshold, 0.5) # is 0.5 ok?
10
+ @non_break_count = opts.fetch(:non_break_count, 100) # is 100 ok?
11
+ @sleep = opts.fetch(:sleep, 1)
12
+ bucket_opts = {
13
+ size: 10,
14
+ per: opts.fetch(:period, 10).to_f / 10
15
+ }
16
+ @bucket = Expeditor::Bucket.new(bucket_opts)
17
+ @breaking = false
18
+ @break_start = nil
19
+ end
20
+
21
+ def success
22
+ @bucket.increment :success
23
+ end
24
+
25
+ def failure
26
+ @bucket.increment :failure
27
+ end
28
+
29
+ def rejection
30
+ @bucket.increment :rejection
31
+ end
32
+
33
+ def timeout
34
+ @bucket.increment :timeout
35
+ end
36
+
37
+ def break
38
+ @bucket.increment :break
39
+ end
40
+
41
+ def dependency
42
+ @bucket.increment :dependency
43
+ end
44
+
45
+ # break circuit?
46
+ def open?
47
+ if @breaking
48
+ if Time.now - @break_start > @sleep
49
+ @breaking = false
50
+ @break_start = nil
51
+ else
52
+ return true
53
+ end
54
+ end
55
+ open = calc_open
56
+ if open
57
+ @breaking = true
58
+ @break_start = Time.now
59
+ end
60
+ open
61
+ end
62
+
63
+ # shutdown thread pool
64
+ # after shutdown, if you create thread, RejectedExecutionError is raised.
65
+ def shutdown
66
+ @executor.shutdown
67
+ end
68
+
69
+ private
70
+
71
+ def calc_open
72
+ s = @bucket.total
73
+ total_count = s.success + s.failure + s.timeout
74
+ if total_count >= [@non_break_count, 1].max
75
+ failure_count = s.failure + s.timeout
76
+ failure_count.to_f / total_count.to_f >= @threshold
77
+ else
78
+ false
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,14 @@
1
+ require 'concurrent/configuration'
2
+ require 'expeditor/services/default'
3
+
4
+ module Expeditor
5
+ module Services
6
+ DEFAULT_SERVICE = Expeditor::Services::Default.new
7
+ private_constant :DEFAULT_SERVICE
8
+
9
+ def default
10
+ DEFAULT_SERVICE
11
+ end
12
+ module_function :default
13
+ end
14
+ end
@@ -0,0 +1,35 @@
1
+ require 'concurrent/configuration'
2
+ require 'expeditor/service'
3
+
4
+ module Expeditor
5
+ module Services
6
+ class Default < Expeditor::Service
7
+ def initialize
8
+ @executor = Concurrent.configuration.global_task_pool
9
+ @bucket = nil
10
+ end
11
+
12
+ def success
13
+ end
14
+
15
+ def failure
16
+ end
17
+
18
+ def rejection
19
+ end
20
+
21
+ def timeout
22
+ end
23
+
24
+ def break
25
+ end
26
+
27
+ def dependency
28
+ end
29
+
30
+ def open?
31
+ false
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,47 @@
1
+ module Expeditor
2
+ class Status
3
+ attr_accessor :success
4
+ attr_accessor :failure
5
+ attr_accessor :rejection
6
+ attr_accessor :timeout
7
+ attr_accessor :break
8
+ attr_accessor :dependency
9
+
10
+ def initialize
11
+ set(0, 0, 0, 0, 0, 0)
12
+ end
13
+
14
+ def increment(type)
15
+ case type
16
+ when :success
17
+ @success += 1
18
+ when :failure
19
+ @failure += 1
20
+ when :rejection
21
+ @rejection += 1
22
+ when :timeout
23
+ @timeout += 1
24
+ when :break
25
+ @break += 1
26
+ when :dependency
27
+ @dependency += 1
28
+ else
29
+ end
30
+ end
31
+
32
+ def reset
33
+ set(0, 0, 0, 0, 0, 0)
34
+ end
35
+
36
+ private
37
+
38
+ def set(s, f, r, t, b, d)
39
+ @success = s
40
+ @failure = f
41
+ @rejection = r
42
+ @timeout = t
43
+ @break = b
44
+ @dependency = d
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,3 @@
1
+ module Expeditor
2
+ VERSION = "0.1.0"
3
+ end
metadata ADDED
@@ -0,0 +1,149 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: expeditor
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - shohei-yasutake
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2015-04-17 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: concurrent-ruby
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '0.8'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '0.8'
27
+ - !ruby/object:Gem::Dependency
28
+ name: concurrent-ruby-ext
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '0.8'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '0.8'
41
+ - !ruby/object:Gem::Dependency
42
+ name: retryable
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>'
46
+ - !ruby/object:Gem::Version
47
+ version: '1.0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>'
53
+ - !ruby/object:Gem::Version
54
+ version: '1.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: bundler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '1.9'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: '1.9'
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.2.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.2.0
97
+ description: Expeditor provides asynchronous execution and fault tolerance for microservices
98
+ email:
99
+ - shohei-yasutake@cookpad.com
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - .gitignore
105
+ - .rspec
106
+ - .travis.yml
107
+ - Gemfile
108
+ - README.md
109
+ - Rakefile
110
+ - bin/console
111
+ - bin/setup
112
+ - examples/example.rb
113
+ - expeditor.gemspec
114
+ - lib/expeditor.rb
115
+ - lib/expeditor/bucket.rb
116
+ - lib/expeditor/command.rb
117
+ - lib/expeditor/errors.rb
118
+ - lib/expeditor/rich_future.rb
119
+ - lib/expeditor/service.rb
120
+ - lib/expeditor/services.rb
121
+ - lib/expeditor/services/default.rb
122
+ - lib/expeditor/status.rb
123
+ - lib/expeditor/version.rb
124
+ homepage: https://github.com/cookpad/expeditor
125
+ licenses: []
126
+ metadata:
127
+ allowed_push_host: 'TODO: Set to ''http://mygemserver.com'' to prevent pushes to
128
+ rubygems.org, or delete to allow pushes to any server.'
129
+ post_install_message:
130
+ rdoc_options: []
131
+ require_paths:
132
+ - lib
133
+ required_ruby_version: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - '>='
136
+ - !ruby/object:Gem::Version
137
+ version: '0'
138
+ required_rubygems_version: !ruby/object:Gem::Requirement
139
+ requirements:
140
+ - - '>='
141
+ - !ruby/object:Gem::Version
142
+ version: '0'
143
+ requirements: []
144
+ rubyforge_project:
145
+ rubygems_version: 2.0.14
146
+ signing_key:
147
+ specification_version: 4
148
+ summary: Expeditor provides asynchronous execution and fault tolerance for microservices
149
+ test_files: []