mamiya 0.0.1.alpha19 → 0.0.1.alpha20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +18 -0
- data/lib/mamiya/agent.rb +25 -30
- data/lib/mamiya/agent/actions.rb +9 -6
- data/lib/mamiya/agent/handlers/task.rb +13 -0
- data/lib/mamiya/agent/task_queue.rb +151 -0
- data/lib/mamiya/agent/tasks/abstract.rb +61 -0
- data/lib/mamiya/agent/tasks/clean.rb +44 -0
- data/lib/mamiya/agent/tasks/fetch.rb +60 -0
- data/lib/mamiya/agent/tasks/notifyable.rb +30 -0
- data/lib/mamiya/cli/client.rb +1 -1
- data/lib/mamiya/master.rb +4 -9
- data/lib/mamiya/master/agent_monitor_handlers.rb +42 -25
- data/lib/mamiya/master/web.rb +22 -7
- data/lib/mamiya/version.rb +1 -1
- data/mamiya.gemspec +1 -1
- data/spec/agent/actions_spec.rb +2 -8
- data/spec/agent/handlers/task_spec.rb +39 -0
- data/spec/agent/task_queue_spec.rb +246 -0
- data/spec/agent/tasks/abstract_spec.rb +58 -0
- data/spec/agent/tasks/clean_spec.rb +72 -0
- data/spec/agent/tasks/fetch_spec.rb +56 -0
- data/spec/agent/tasks/notifyable_spec.rb +37 -0
- data/spec/agent_spec.rb +33 -54
- data/spec/master/agent_monitor_spec.rb +155 -69
- data/spec/master/web_spec.rb +340 -1
- data/spec/master_spec.rb +0 -21
- metadata +22 -10
- data/lib/mamiya/agent/fetcher.rb +0 -165
- data/lib/mamiya/agent/handlers/fetch.rb +0 -78
- data/spec/agent/fetcher_spec.rb +0 -237
- data/spec/agent/handlers/fetch_spec.rb +0 -127
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'mamiya/agent/tasks/notifyable'
|
2
|
+
require 'mamiya/steps/fetch'
|
3
|
+
require 'mamiya/storages/abstract'
|
4
|
+
|
5
|
+
module Mamiya
|
6
|
+
class Agent
|
7
|
+
module Tasks
|
8
|
+
class Fetch < Notifyable
|
9
|
+
def run
|
10
|
+
logger.info "Fetching #{application}/#{package}"
|
11
|
+
|
12
|
+
take_interval
|
13
|
+
step.run!
|
14
|
+
order_cleaning
|
15
|
+
rescue Mamiya::Storages::Abstract::AlreadyFetched
|
16
|
+
logger.info "It has already fetched; skipping."
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def take_interval
|
22
|
+
fetch_sleep = config[:fetch_sleep]
|
23
|
+
wait = rand(fetch_sleep)
|
24
|
+
|
25
|
+
@logger.info "Sleeping #{wait} sec before starting fetch"
|
26
|
+
rand(wait)
|
27
|
+
end
|
28
|
+
|
29
|
+
def order_cleaning
|
30
|
+
task_queue.enqueue(:clean, {})
|
31
|
+
end
|
32
|
+
|
33
|
+
def application
|
34
|
+
task['app']
|
35
|
+
end
|
36
|
+
|
37
|
+
def package
|
38
|
+
task['pkg']
|
39
|
+
end
|
40
|
+
|
41
|
+
def destination
|
42
|
+
@destination ||= File.join(packages_dir, application)
|
43
|
+
end
|
44
|
+
|
45
|
+
def packages_dir
|
46
|
+
@packages_dir ||= config && config[:packages_dir]
|
47
|
+
end
|
48
|
+
|
49
|
+
def step
|
50
|
+
@step ||= Mamiya::Steps::Fetch.new(
|
51
|
+
application: application,
|
52
|
+
package: package,
|
53
|
+
destination: destination,
|
54
|
+
config: config,
|
55
|
+
)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'mamiya/agent'
|
2
|
+
require 'mamiya/agent/tasks/abstract'
|
3
|
+
|
4
|
+
module Mamiya
|
5
|
+
class Agent
|
6
|
+
module Tasks
|
7
|
+
class Notifyable < Abstract
|
8
|
+
def execute
|
9
|
+
agent.trigger('task', action: 'start',
|
10
|
+
task: task
|
11
|
+
)
|
12
|
+
|
13
|
+
super
|
14
|
+
|
15
|
+
ensure
|
16
|
+
if error
|
17
|
+
agent.trigger('task', action: 'error',
|
18
|
+
error: error.class.name,
|
19
|
+
task: task,
|
20
|
+
)
|
21
|
+
else
|
22
|
+
agent.trigger('task', action: 'finish',
|
23
|
+
task: task,
|
24
|
+
)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/mamiya/cli/client.rb
CHANGED
@@ -10,7 +10,7 @@ require 'thor'
|
|
10
10
|
module Mamiya
|
11
11
|
class CLI < Thor
|
12
12
|
class Client < Thor
|
13
|
-
class_option :master, aliases: '-u', type: :string
|
13
|
+
class_option :master, aliases: '-u', type: :string, default: 'http://localhost:7761/'
|
14
14
|
class_option :application, aliases: %w(-a --app), type: :string
|
15
15
|
|
16
16
|
desc "list-applications", "list applications"
|
data/lib/mamiya/master.rb
CHANGED
@@ -27,7 +27,7 @@ module Mamiya
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def start
|
30
|
-
# Override and stop starting
|
30
|
+
# Override and stop starting task_queue
|
31
31
|
web_start
|
32
32
|
serf_start
|
33
33
|
monitor_start
|
@@ -38,11 +38,6 @@ module Mamiya
|
|
38
38
|
super
|
39
39
|
end
|
40
40
|
|
41
|
-
# XXX: dupe? Mamiya::Agent::Actions#distribute
|
42
|
-
def distribute(application, package)
|
43
|
-
trigger(:fetch, coalesce: false, application: application, package: package)
|
44
|
-
end
|
45
|
-
|
46
41
|
def storage(app)
|
47
42
|
config.storage_class.new(
|
48
43
|
config[:storage].merge(
|
@@ -89,9 +84,9 @@ module Mamiya
|
|
89
84
|
options = config[:web] || {}
|
90
85
|
rack_options = {
|
91
86
|
app: self.web,
|
92
|
-
Port: options[:port].to_i,
|
93
|
-
Host: options[:bind],
|
94
|
-
environment: options[:environment],
|
87
|
+
Port: options[:port] ? options[:port].to_i : 7761,
|
88
|
+
Host: options[:bind] || '0.0.0.0', # TODO: IPv6
|
89
|
+
environment: options[:environment] || :development,
|
95
90
|
server: options[:server],
|
96
91
|
Logger: logger['web']
|
97
92
|
}
|
@@ -2,55 +2,72 @@ require 'mamiya/master'
|
|
2
2
|
|
3
3
|
module Mamiya
|
4
4
|
class Master
|
5
|
+
# XXX: TODO:
|
5
6
|
module AgentMonitorHandlers
|
6
|
-
def
|
7
|
-
|
8
|
-
status['fetcher']['pending'] = payload['pending']
|
7
|
+
def task__start(status, payload, event)
|
8
|
+
task = payload['task']
|
9
9
|
|
10
|
-
status['
|
11
|
-
status['
|
10
|
+
status['queues'] ||= {}
|
11
|
+
status['queues'][task['task']] ||= {'queue' => [], 'working' => nil}
|
12
|
+
|
13
|
+
status['queues'][task['task']]['working'] = task
|
14
|
+
status['queues'][task['task']]['queue'].delete task
|
12
15
|
end
|
13
16
|
|
14
|
-
def
|
15
|
-
|
16
|
-
status['fetcher']['fetching'] = [payload['application'], payload['package']]
|
17
|
+
def task__finalize(status, payload, event)
|
18
|
+
task = payload['task']
|
17
19
|
|
18
|
-
|
20
|
+
status['queues'] ||= {}
|
21
|
+
status['queues'][task['task']] ||= {'queue' => [], 'working' => nil}
|
19
22
|
|
20
|
-
status['
|
21
|
-
|
23
|
+
s = status['queues'][task['task']]
|
24
|
+
if s['working'] == task
|
25
|
+
s['working'] = nil
|
26
|
+
end
|
27
|
+
status['queues'][task['task']]['queue'].delete task
|
22
28
|
end
|
23
29
|
|
24
|
-
def
|
25
|
-
|
30
|
+
def task__finish(status, payload, event)
|
31
|
+
task = payload['task']
|
32
|
+
logger.error "#{status['name']} finished task #{task['task']}: #{payload['error']}"
|
26
33
|
|
27
|
-
|
34
|
+
task__finalize(status, payload, event)
|
28
35
|
|
29
|
-
|
30
|
-
|
36
|
+
method_name = "task___#{task['task']}__finish"
|
37
|
+
if self.respond_to?(method_name)
|
38
|
+
__send__ method_name, status, task
|
31
39
|
end
|
32
40
|
end
|
33
41
|
|
34
|
-
def
|
35
|
-
|
42
|
+
def task__error(status, payload, event)
|
43
|
+
task = payload['task']
|
44
|
+
logger.error "#{status['name']} failed task #{task['task']}: #{payload['error']}"
|
36
45
|
|
37
|
-
|
46
|
+
task__finalize(status, payload, event)
|
38
47
|
|
39
|
-
|
40
|
-
|
48
|
+
method_name = "task___#{task['task']}__error"
|
49
|
+
if self.respond_to?(method_name)
|
50
|
+
__send__ method_name, status, task, error
|
41
51
|
end
|
52
|
+
end
|
53
|
+
|
42
54
|
|
55
|
+
|
56
|
+
# XXX: move task finish handlers into tasks/
|
57
|
+
def task___fetch__finish(status, task)
|
43
58
|
status['packages'] ||= {}
|
44
|
-
status['packages'][
|
45
|
-
|
59
|
+
status['packages'][task['app']] ||= []
|
60
|
+
|
61
|
+
unless status['packages'][task['app']].include?(task['pkg'])
|
62
|
+
status['packages'][task['app']] << task['pkg']
|
63
|
+
end
|
46
64
|
end
|
47
65
|
|
48
|
-
def
|
66
|
+
def pkg__remove(status, payload, event)
|
49
67
|
status['packages'] ||= {}
|
50
68
|
packages = status['packages'][payload['application']]
|
51
69
|
packages.delete(payload['package']) if packages
|
52
70
|
end
|
53
|
-
|
54
71
|
end
|
55
72
|
end
|
56
73
|
end
|
data/lib/mamiya/master/web.rb
CHANGED
@@ -21,7 +21,8 @@ module Mamiya
|
|
21
21
|
end
|
22
22
|
|
23
23
|
get '/' do
|
24
|
-
|
24
|
+
content_type 'text/plain'
|
25
|
+
"mamiya v#{Mamiya::VERSION}\n"
|
25
26
|
end
|
26
27
|
|
27
28
|
get '/packages/:application' do
|
@@ -88,15 +89,29 @@ module Mamiya
|
|
88
89
|
|
89
90
|
statuses.each do |name, status|
|
90
91
|
next if status["master"]
|
91
|
-
|
92
|
-
|
92
|
+
queue = status["queues"] && status["queues"]["fetch"]
|
93
|
+
packages = status["packages"] && status["packages"][params[:application]]
|
94
|
+
|
95
|
+
task_matcher = -> (task) do
|
96
|
+
task["task"] == "fetch" &&
|
97
|
+
task["app"] == params[:application] &&
|
98
|
+
task["pkg"] == params[:package]
|
99
|
+
end
|
100
|
+
|
101
|
+
|
102
|
+
case
|
103
|
+
when packages && packages.include?(params[:package])
|
93
104
|
|
94
105
|
result[:distributed] << name
|
95
|
-
|
106
|
+
|
107
|
+
when queue && queue["working"] && task_matcher.call(queue['working'])
|
108
|
+
|
96
109
|
result[:fetching] << name
|
97
|
-
|
98
|
-
|
110
|
+
|
111
|
+
when queue['queue'].any?(&task_matcher)
|
112
|
+
|
99
113
|
result[:queued] << name
|
114
|
+
|
100
115
|
else
|
101
116
|
result[:not_distributed] << name
|
102
117
|
end
|
@@ -113,7 +128,7 @@ module Mamiya
|
|
113
128
|
when 0 < result[:queued_count] || 0 < result[:fetching_count]
|
114
129
|
status = :distributing
|
115
130
|
when 0 < result[:distributed_count] && result[:distributed_count] < total
|
116
|
-
status = :
|
131
|
+
status = :partially_distributed
|
117
132
|
when result[:distributed_count] == total
|
118
133
|
status = :distributed
|
119
134
|
else
|
data/lib/mamiya/version.rb
CHANGED
data/mamiya.gemspec
CHANGED
@@ -19,7 +19,7 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
21
|
spec.add_runtime_dependency "thor", ">= 0.18.1"
|
22
|
-
spec.add_runtime_dependency "aws-sdk-core", "2.0.0.
|
22
|
+
spec.add_runtime_dependency "aws-sdk-core", "2.0.0.rc15"
|
23
23
|
spec.add_runtime_dependency "term-ansicolor", ">= 1.3.0"
|
24
24
|
unless ENV["MAMIYA_VILLEIN_PATH"]
|
25
25
|
spec.add_runtime_dependency "villein", ">= 0.3.2"
|
data/spec/agent/actions_spec.rb
CHANGED
@@ -10,7 +10,6 @@ require_relative '../support/dummy_serf.rb'
|
|
10
10
|
|
11
11
|
describe Mamiya::Agent::Actions do
|
12
12
|
let(:serf) { DummySerf.new }
|
13
|
-
let(:fetcher) { double('fetcher', start!: nil) }
|
14
13
|
|
15
14
|
let(:config) do
|
16
15
|
{serf: {agent: {rpc_addr: '127.0.0.1:17373', bind: '127.0.0.1:17946'}}}
|
@@ -18,7 +17,6 @@ describe Mamiya::Agent::Actions do
|
|
18
17
|
|
19
18
|
before do
|
20
19
|
allow(Villein::Agent).to receive(:new).and_return(serf)
|
21
|
-
allow(Mamiya::Agent::Fetcher).to receive(:new).and_return(fetcher)
|
22
20
|
end
|
23
21
|
|
24
22
|
subject(:agent) { Mamiya::Agent.new(config) }
|
@@ -26,13 +24,9 @@ describe Mamiya::Agent::Actions do
|
|
26
24
|
|
27
25
|
describe "#distribute" do
|
28
26
|
it "sends fetch request" do
|
29
|
-
expect(
|
30
|
-
'mamiya:fetch',
|
31
|
-
{application: 'app', package: 'pkg'}.to_json,
|
32
|
-
coalesce: false
|
33
|
-
)
|
27
|
+
expect(agent).to receive(:trigger).with('task', task: 'fetch', app: 'myapp', pkg: 'mypkg', coalesce: false)
|
34
28
|
|
35
|
-
agent.distribute('
|
29
|
+
agent.distribute('myapp', 'mypkg')
|
36
30
|
end
|
37
31
|
end
|
38
32
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'villein/event'
|
5
|
+
|
6
|
+
require 'mamiya/agent/handlers/task'
|
7
|
+
|
8
|
+
describe Mamiya::Agent::Handlers::Task do
|
9
|
+
let(:event) do
|
10
|
+
Villein::Event.new(
|
11
|
+
{
|
12
|
+
'SERF_EVENT' => 'user',
|
13
|
+
'SERF_USER_EVENT' => 'mamiya:task',
|
14
|
+
},
|
15
|
+
payload: {
|
16
|
+
task: 'fetch',
|
17
|
+
application: 'app',
|
18
|
+
package: 'package',
|
19
|
+
}.to_json,
|
20
|
+
)
|
21
|
+
end
|
22
|
+
|
23
|
+
let(:task_queue) { double('task_queue', enqueue: nil) }
|
24
|
+
|
25
|
+
let(:agent) do
|
26
|
+
double('agent', task_queue: task_queue)
|
27
|
+
end
|
28
|
+
|
29
|
+
subject(:handler) { described_class.new(agent, event) }
|
30
|
+
|
31
|
+
before do
|
32
|
+
end
|
33
|
+
|
34
|
+
it "enqueue a request" do
|
35
|
+
expect(task_queue).to receive(:enqueue).with(:fetch, 'task' => 'fetch', 'application' => 'app', 'package' => 'package')
|
36
|
+
|
37
|
+
handler.run!
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,246 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'thread'
|
3
|
+
require 'mamiya/agent/tasks/abstract'
|
4
|
+
require 'mamiya/agent/task_queue'
|
5
|
+
|
6
|
+
describe Mamiya::Agent::TaskQueue do
|
7
|
+
let(:agent) do
|
8
|
+
double('agent')
|
9
|
+
end
|
10
|
+
|
11
|
+
let(:task_class_root) do
|
12
|
+
Class.new(Mamiya::Agent::Tasks::Abstract) do
|
13
|
+
def self.runs
|
14
|
+
@runs ||= []
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.locks
|
18
|
+
@locks ||= {}
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.locks_lock
|
22
|
+
@locks_lock ||= Mutex.new
|
23
|
+
end
|
24
|
+
|
25
|
+
def execute
|
26
|
+
self.class.runs << task.dup
|
27
|
+
if task['wait']
|
28
|
+
begin
|
29
|
+
queue = Queue.new
|
30
|
+
self.class.locks_lock.synchronize do
|
31
|
+
self.class.locks[self.task] = queue
|
32
|
+
end
|
33
|
+
queue.pop
|
34
|
+
ensure
|
35
|
+
self.class.locks_lock.synchronize do
|
36
|
+
self.class.locks.delete self.task
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
let(:task_class_a) do
|
46
|
+
Class.new(task_class_root) do
|
47
|
+
def self.identifier
|
48
|
+
'a'
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
let(:task_class_b) do
|
54
|
+
Class.new(task_class_root) do
|
55
|
+
def self.identifier
|
56
|
+
'b'
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
subject(:queue) do
|
62
|
+
described_class.new(agent, task_classes: [task_class_a, task_class_b])
|
63
|
+
end
|
64
|
+
|
65
|
+
describe "lifecycle (#start!, #stop!)" do
|
66
|
+
it "can start and stop" do
|
67
|
+
expect(queue).not_to be_running
|
68
|
+
expect(queue.worker_threads).to be_nil
|
69
|
+
|
70
|
+
queue.start!
|
71
|
+
|
72
|
+
expect(queue).to be_running
|
73
|
+
|
74
|
+
expect(queue.worker_threads).to be_a_kind_of(Hash)
|
75
|
+
expect(queue.worker_threads.values.all? { |v| v.kind_of?(Thread) }).to be_true
|
76
|
+
expect(queue.worker_threads.values.all? { |v| v.alive? }).to be_true
|
77
|
+
threads = queue.worker_threads.dup
|
78
|
+
|
79
|
+
queue.stop!
|
80
|
+
|
81
|
+
expect(queue).not_to be_running
|
82
|
+
expect(queue.worker_threads).to be_nil
|
83
|
+
expect(threads.each_value.all? { |v| !v.alive? }).to be_true
|
84
|
+
end
|
85
|
+
|
86
|
+
it "can stop gracefully"
|
87
|
+
end
|
88
|
+
|
89
|
+
describe "work loop (#enqueue, #running?, #working, #status)" do
|
90
|
+
after do
|
91
|
+
queue.stop! if queue.running?
|
92
|
+
end
|
93
|
+
|
94
|
+
it "run enqueued task" do
|
95
|
+
queue.start!
|
96
|
+
|
97
|
+
queue.enqueue(:a, 'foo' => '1')
|
98
|
+
queue.enqueue(:a, 'foo' => '2')
|
99
|
+
100.times { break if task_class_a.runs.size == 2; sleep 0.01 }
|
100
|
+
|
101
|
+
expect(task_class_a.runs.size).to eq 2
|
102
|
+
expect(task_class_a.runs[0]['foo']).to eq '1'
|
103
|
+
expect(task_class_a.runs[1]['foo']).to eq '2'
|
104
|
+
end
|
105
|
+
|
106
|
+
describe "#working?" do
|
107
|
+
it "returns true if there're any working tasks" do
|
108
|
+
queue.start!
|
109
|
+
|
110
|
+
expect(queue).not_to be_working
|
111
|
+
|
112
|
+
queue.enqueue(:a, 'wait' => true, 'id' => 1)
|
113
|
+
100.times { break unless task_class_a.locks.empty?; sleep 0.01 }
|
114
|
+
expect(task_class_a.locks).not_to be_empty
|
115
|
+
expect(queue).to be_working
|
116
|
+
|
117
|
+
task_class_a.locks.values.last << true
|
118
|
+
|
119
|
+
100.times { break unless queue.working?; sleep 0.01 }
|
120
|
+
expect(queue).not_to be_working
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
|
125
|
+
describe "#status" do
|
126
|
+
it "shows status" do
|
127
|
+
queue.start!
|
128
|
+
|
129
|
+
expect(queue.status[:a][:working]).to be_nil
|
130
|
+
expect(queue.status[:a][:queue]).to be_a_kind_of(Array)
|
131
|
+
expect(queue.status[:a][:queue]).to be_empty
|
132
|
+
|
133
|
+
queue.enqueue(:a, 'wait' => true, 'id' => 1)
|
134
|
+
|
135
|
+
100.times { break unless task_class_a.locks.empty?; sleep 0.01 }
|
136
|
+
expect(task_class_a.locks).not_to be_empty
|
137
|
+
expect(queue.status[:a][:working]).to eq('wait' => true, 'id' => 1)
|
138
|
+
|
139
|
+
queue.enqueue(:a, 'id' => 2)
|
140
|
+
100.times { break unless queue.status[:a][:queue].empty?; sleep 0.01 }
|
141
|
+
expect(queue.status[:a][:queue].size).to eq 1
|
142
|
+
expect(queue.status[:a][:queue].first).to eq('id' => 2)
|
143
|
+
|
144
|
+
task_class_a.locks.values.last << true
|
145
|
+
|
146
|
+
100.times { break unless queue.status[:a][:working]; sleep 0.01 }
|
147
|
+
expect(queue.status[:a][:working]).to be_nil
|
148
|
+
expect(queue.status[:a][:queue]).to be_empty
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
context "with multiple task classes" do
|
153
|
+
it "run enqueued task" do
|
154
|
+
queue.start!
|
155
|
+
|
156
|
+
queue.enqueue(:a, 'foo' => '1')
|
157
|
+
queue.enqueue(:b, 'foo' => '2')
|
158
|
+
100.times { break if task_class_a.runs.size == 1 && task_class_b.runs.size == 1; sleep 0.01 }
|
159
|
+
|
160
|
+
expect(task_class_a.runs.size).to eq 1
|
161
|
+
expect(task_class_b.runs.size).to eq 1
|
162
|
+
expect(task_class_a.runs[0]['foo']).to eq '1'
|
163
|
+
expect(task_class_b.runs[0]['foo']).to eq '2'
|
164
|
+
end
|
165
|
+
|
166
|
+
it "run enqueued task parallel" do
|
167
|
+
queue.start!
|
168
|
+
|
169
|
+
queue.enqueue(:a, 'foo' => '1', 'wait' => true)
|
170
|
+
queue.enqueue(:b, 'foo' => '2', 'wait' => true)
|
171
|
+
100.times { break if task_class_a.locks.size == 1 && task_class_b.locks.size == 1; sleep 0.01 }
|
172
|
+
|
173
|
+
expect(task_class_a.locks.size).to eq 1
|
174
|
+
expect(task_class_b.locks.size).to eq 1
|
175
|
+
task_class_a.locks.each_value.first << true
|
176
|
+
task_class_b.locks.each_value.first << true
|
177
|
+
|
178
|
+
expect(task_class_a.runs.size).to eq 1
|
179
|
+
expect(task_class_b.runs.size).to eq 1
|
180
|
+
expect(task_class_a.runs[0]['foo']).to eq '1'
|
181
|
+
expect(task_class_b.runs[0]['foo']).to eq '2'
|
182
|
+
end
|
183
|
+
|
184
|
+
describe "#status" do
|
185
|
+
it "shows status for each task class" do
|
186
|
+
queue.start!
|
187
|
+
|
188
|
+
expect(queue.status[:a][:working]).to be_nil
|
189
|
+
expect(queue.status[:a][:queue]).to be_a_kind_of(Array)
|
190
|
+
expect(queue.status[:a][:queue]).to be_empty
|
191
|
+
|
192
|
+
expect(queue.status[:b][:working]).to be_nil
|
193
|
+
expect(queue.status[:b][:queue]).to be_a_kind_of(Array)
|
194
|
+
expect(queue.status[:b][:queue]).to be_empty
|
195
|
+
|
196
|
+
queue.enqueue(:a, 'wait' => true, 'id' => 1)
|
197
|
+
queue.enqueue(:b, 'wait' => true, 'id' => 2)
|
198
|
+
|
199
|
+
100.times { break if !task_class_a.locks.empty? && !task_class_a.locks.empty?; sleep 0.01 }
|
200
|
+
expect(task_class_a.locks).not_to be_empty
|
201
|
+
expect(task_class_b.locks).not_to be_empty
|
202
|
+
|
203
|
+
expect(queue.status[:a][:working]).to eq('wait' => true, 'id' => 1)
|
204
|
+
expect(queue.status[:b][:working]).to eq('wait' => true, 'id' => 2)
|
205
|
+
|
206
|
+
queue.enqueue(:a, 'id' => 3)
|
207
|
+
queue.enqueue(:b, 'id' => 4)
|
208
|
+
100.times { break if !queue.status[:a][:queue].empty? && !queue.status[:b][:queue].empty?; sleep 0.01 }
|
209
|
+
expect(queue.status[:a][:queue].size).to eq 1
|
210
|
+
expect(queue.status[:a][:queue].first).to eq('id' => 3)
|
211
|
+
expect(queue.status[:b][:queue].size).to eq 1
|
212
|
+
expect(queue.status[:b][:queue].first).to eq('id' => 4)
|
213
|
+
|
214
|
+
task_class_a.locks.values.last << true
|
215
|
+
task_class_b.locks.values.last << true
|
216
|
+
|
217
|
+
100.times { break if !queue.status[:a][:working] && !queue.status[:b][:working]; sleep 0.01 }
|
218
|
+
expect(queue.status[:a][:working]).to be_nil
|
219
|
+
expect(queue.status[:a][:queue]).to be_empty
|
220
|
+
expect(queue.status[:b][:working]).to be_nil
|
221
|
+
expect(queue.status[:b][:queue]).to be_empty
|
222
|
+
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
describe "#working?" do
|
227
|
+
it "returns true if there're any working tasks" do
|
228
|
+
queue.start!
|
229
|
+
|
230
|
+
expect(queue).not_to be_working
|
231
|
+
|
232
|
+
queue.enqueue(:a, 'wait' => true, 'id' => 1)
|
233
|
+
100.times { break unless task_class_a.locks.empty?; sleep 0.01 }
|
234
|
+
expect(task_class_a.locks).not_to be_empty
|
235
|
+
expect(queue).to be_working
|
236
|
+
|
237
|
+
task_class_a.locks.values.last << true
|
238
|
+
|
239
|
+
100.times { break unless queue.working?; sleep 0.01 }
|
240
|
+
expect(queue).not_to be_working
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|