hydroponic_bean 1.1.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.ruby-version +1 -0
- data/CHANGELOG.md +6 -0
- data/Gemfile +3 -0
- data/lib/hydroponic_bean/commands.rb +12 -0
- data/lib/hydroponic_bean/commands/other.rb +13 -35
- data/lib/hydroponic_bean/commands/producer.rb +5 -2
- data/lib/hydroponic_bean/commands/tube.rb +1 -11
- data/lib/hydroponic_bean/commands/worker.rb +76 -0
- data/lib/hydroponic_bean/connection.rb +16 -0
- data/lib/hydroponic_bean/data.rb +99 -1
- data/lib/hydroponic_bean/job.rb +68 -8
- data/lib/hydroponic_bean/protocol.rb +3 -1
- data/lib/hydroponic_bean/tube.rb +48 -9
- data/lib/hydroponic_bean/version.rb +1 -1
- data/protocol.txt +715 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9a0b2b9b746d52cb5ed0499c0ac2f8dc45213231
|
4
|
+
data.tar.gz: 4f23cda0c022146a7c5b99cb043363cc6a59ae8d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0e0d194be39a4af19c053f946e9eda3740e3e1dbf17791608920abbd2bf5e2eb178804436eea5ec3c7be9e2177afef5991a52e81256133b943eab42120f16aa1
|
7
|
+
data.tar.gz: de72c9b293986ced6d10fc67952e8d1b2af2ac50896bbea77079931cc8bd4f96e1e994e82842f9cf008fbe21890bc0a9b43ff2bcd1ed12dda8ec01386f240432
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.3.0
|
data/CHANGELOG.md
CHANGED
data/Gemfile
CHANGED
@@ -9,5 +9,17 @@ module HydroponicBean
|
|
9
9
|
include HydroponicBean::Commands::Worker
|
10
10
|
include HydroponicBean::Commands::Tube
|
11
11
|
include HydroponicBean::Commands::Other
|
12
|
+
|
13
|
+
# Find a job by id and yield it
|
14
|
+
#
|
15
|
+
# Outputs Protocol::NOT_FOUND if the block returns false
|
16
|
+
# or the job is not found
|
17
|
+
def for_job(id)
|
18
|
+
job = HydroponicBean.find_job(id)
|
19
|
+
if !job || !yield(job)
|
20
|
+
output(Protocol::NOT_FOUND)
|
21
|
+
return false
|
22
|
+
end
|
23
|
+
end
|
12
24
|
end
|
13
25
|
end
|
@@ -2,12 +2,13 @@ module HydroponicBean
|
|
2
2
|
module Commands
|
3
3
|
module Other
|
4
4
|
def peek(stream, id = nil)
|
5
|
-
id
|
6
|
-
|
7
|
-
|
5
|
+
for_job(id) do |job|
|
6
|
+
peek_output(job)
|
7
|
+
end
|
8
8
|
end
|
9
9
|
|
10
10
|
def peek_ready(stream)
|
11
|
+
HydroponicBean.update_time!
|
11
12
|
peek_output current_tube.ready_jobs.first
|
12
13
|
end
|
13
14
|
|
@@ -16,46 +17,23 @@ module HydroponicBean
|
|
16
17
|
end
|
17
18
|
|
18
19
|
def peek_delayed(stream)
|
20
|
+
HydroponicBean.update_time!
|
19
21
|
peek_output current_tube.delayed_jobs.first
|
20
22
|
end
|
21
23
|
|
22
24
|
def stats_job(stream, id)
|
23
|
-
id
|
24
|
-
|
25
|
-
|
26
|
-
output(
|
27
|
-
return false
|
25
|
+
for_job(id) do |job|
|
26
|
+
stats = job.serialize_stats.to_yaml
|
27
|
+
output("OK #{stats.length}\r\n")
|
28
|
+
output("#{stats}\r\n")
|
28
29
|
end
|
29
|
-
|
30
|
-
stats = job.serialize_stats.to_yaml
|
31
|
-
output("OK #{stats.length}\r\n")
|
32
|
-
output("#{stats}\r\n")
|
33
30
|
end
|
34
31
|
|
35
32
|
def kick_job(stream, id)
|
36
|
-
id
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
return false
|
41
|
-
end
|
42
|
-
job.kick
|
43
|
-
output("KICKED\r\n")
|
44
|
-
end
|
45
|
-
|
46
|
-
def watch(stream, tube_name)
|
47
|
-
watched_tube_names << tube_name
|
48
|
-
watched_tube_names.uniq!
|
49
|
-
output("WATCHING #{watched_tube_names.count}\r\n")
|
50
|
-
end
|
51
|
-
|
52
|
-
def ignore(stream, tube_name)
|
53
|
-
watched_tube_names.delete(tube_name)
|
54
|
-
if watched_tube_names.empty?
|
55
|
-
watched_tube_names << tube_name
|
56
|
-
output("NOT_IGNORED\r\n")
|
57
|
-
else
|
58
|
-
output("WATCHING #{watched_tube_names.count}\r\n")
|
33
|
+
for_job(id) do |job|
|
34
|
+
if job.kick
|
35
|
+
output("KICKED\r\n")
|
36
|
+
end
|
59
37
|
end
|
60
38
|
end
|
61
39
|
|
@@ -7,6 +7,9 @@ module HydroponicBean
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def put(stream, pri, delay, ttr, bytes)
|
10
|
+
# Mark this connection as a producer
|
11
|
+
producer!
|
12
|
+
|
10
13
|
bytes = bytes.to_i
|
11
14
|
data = stream.read(bytes)
|
12
15
|
|
@@ -20,8 +23,8 @@ module HydroponicBean
|
|
20
23
|
return false
|
21
24
|
end
|
22
25
|
|
23
|
-
|
24
|
-
output("INSERTED #{id}\r\n")
|
26
|
+
job = create_job(pri, delay, ttr, data)
|
27
|
+
output("INSERTED #{job.id}\r\n")
|
25
28
|
end
|
26
29
|
end
|
27
30
|
end
|
@@ -1,17 +1,6 @@
|
|
1
1
|
module HydroponicBean
|
2
2
|
module Commands
|
3
3
|
module Tube
|
4
|
-
def delete(stream, id)
|
5
|
-
job = HydroponicBean.jobs[id.to_i - 1]
|
6
|
-
if job
|
7
|
-
job.delete
|
8
|
-
output("DELETED\r\n")
|
9
|
-
else
|
10
|
-
output(Protocol::NOT_FOUND)
|
11
|
-
return false
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
4
|
def list_tubes(stream)
|
16
5
|
tubes = HydroponicBean.tubes.keys.to_yaml
|
17
6
|
output("OK #{tubes.length}\r\n")
|
@@ -48,6 +37,7 @@ module HydroponicBean
|
|
48
37
|
end
|
49
38
|
|
50
39
|
def kick(stream, bound)
|
40
|
+
HydroponicBean.update_time!
|
51
41
|
bound = bound.to_i
|
52
42
|
tube = current_tube
|
53
43
|
output("KICKED #{tube.kick(bound)}\r\n")
|
@@ -1,6 +1,82 @@
|
|
1
1
|
module HydroponicBean
|
2
2
|
module Commands
|
3
3
|
module Worker
|
4
|
+
def reserve(stream)
|
5
|
+
reserve_with_timeout(stream, -1)
|
6
|
+
end
|
7
|
+
|
8
|
+
def reserve_with_timeout(stream, seconds)
|
9
|
+
# Mark this connection as a worker
|
10
|
+
worker!
|
11
|
+
|
12
|
+
if deadline_soon?
|
13
|
+
output("DEADLINE_SOON\r\n")
|
14
|
+
return true
|
15
|
+
end
|
16
|
+
|
17
|
+
seconds = seconds.to_i
|
18
|
+
|
19
|
+
if job = wait_for_job(seconds)
|
20
|
+
output("RESERVED #{job.id} #{job.data.length}\r\n")
|
21
|
+
output("#{job.data}\r\n")
|
22
|
+
else
|
23
|
+
output("TIMED_OUT\r\n")
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def release(stream, id, pri, delay)
|
28
|
+
# We don't have a BURIED response here
|
29
|
+
for_job(id) do |job|
|
30
|
+
if job.release(self, pri, delay)
|
31
|
+
output("RELEASED\r\n")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def bury(stream, id, pri)
|
37
|
+
for_job(id) do |job|
|
38
|
+
if job.bury(self, pri)
|
39
|
+
output("BURIED\r\n")
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def delete(stream, id)
|
45
|
+
for_job(id) do |job|
|
46
|
+
if job.delete(self)
|
47
|
+
output("DELETED\r\n")
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def touch(stream, id)
|
53
|
+
for_job(id) do |job|
|
54
|
+
if job.touch(self)
|
55
|
+
output("TOUCHED\r\n")
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def watch(stream, tube_name)
|
61
|
+
watched_tube_names << tube_name
|
62
|
+
watched_tube_names.uniq!
|
63
|
+
output_watching
|
64
|
+
end
|
65
|
+
|
66
|
+
def ignore(stream, tube_name)
|
67
|
+
watched_tube_names.delete(tube_name)
|
68
|
+
if watched_tube_names.empty?
|
69
|
+
watched_tube_names << tube_name
|
70
|
+
output("NOT_IGNORED\r\n")
|
71
|
+
else
|
72
|
+
output_watching
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
protected
|
77
|
+
def output_watching
|
78
|
+
output("WATCHING #{watched_tube_names.count}\r\n")
|
79
|
+
end
|
4
80
|
end
|
5
81
|
end
|
6
82
|
end
|
@@ -4,8 +4,23 @@ module HydroponicBean
|
|
4
4
|
class Connection
|
5
5
|
include HydroponicBean::Protocol
|
6
6
|
|
7
|
+
attr_accessor :waiting
|
8
|
+
alias_method :waiting?, :waiting
|
9
|
+
|
7
10
|
def initialize
|
8
11
|
@_read, @_write = IO.pipe
|
12
|
+
@worker, @producer = false
|
13
|
+
@waiting = false
|
14
|
+
HydroponicBean.add_connection(self)
|
15
|
+
end
|
16
|
+
|
17
|
+
def worker?; @worker; end
|
18
|
+
def worker!; @worker = true; end
|
19
|
+
def producer?; @producer; end
|
20
|
+
def producer!; @producer = true; end
|
21
|
+
|
22
|
+
def closed?
|
23
|
+
@_write.closed?
|
9
24
|
end
|
10
25
|
|
11
26
|
# Necessary interface used by beaneater
|
@@ -24,6 +39,7 @@ module HydroponicBean
|
|
24
39
|
def close
|
25
40
|
@_read.close
|
26
41
|
@_write.close
|
42
|
+
HydroponicBean.remove_connection(self)
|
27
43
|
end
|
28
44
|
|
29
45
|
protected
|
data/lib/hydroponic_bean/data.rb
CHANGED
@@ -1,15 +1,56 @@
|
|
1
|
+
require 'timeout'
|
2
|
+
|
1
3
|
require 'hydroponic_bean/tube'
|
2
4
|
require 'hydroponic_bean/job'
|
3
5
|
|
4
6
|
module HydroponicBean
|
7
|
+
def self.clear
|
8
|
+
tubes.clear
|
9
|
+
jobs.clear
|
10
|
+
connections.clear
|
11
|
+
commands.clear
|
12
|
+
end
|
13
|
+
|
5
14
|
def self.tubes
|
6
15
|
@tubes ||= Hash.new{|h, k| h[k] = Tube.new(k)}
|
7
16
|
end
|
8
17
|
|
18
|
+
def self.find_job(id)
|
19
|
+
id = id.to_i
|
20
|
+
if id == 0
|
21
|
+
return nil
|
22
|
+
else
|
23
|
+
job = jobs[id - 1]
|
24
|
+
job&.update_time!
|
25
|
+
return job
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
9
29
|
def self.jobs
|
10
30
|
@jobs ||= []
|
11
31
|
end
|
12
32
|
|
33
|
+
def self.update_time!
|
34
|
+
jobs.each(&:update_time!)
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.connections
|
38
|
+
@connections ||= []
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.add_connection(connection)
|
42
|
+
connections.push(connection)
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.remove_connection(connection)
|
46
|
+
connections.delete(connection)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Keep track of commands for stats
|
50
|
+
def self.commands
|
51
|
+
@commands ||= Hash.new{|h, k| h[k] = 0}
|
52
|
+
end
|
53
|
+
|
13
54
|
module Data
|
14
55
|
def current_tube_name
|
15
56
|
@current_tube_name ||= 'default'
|
@@ -23,12 +64,69 @@ module HydroponicBean
|
|
23
64
|
@watched_tube_names ||= ['default']
|
24
65
|
end
|
25
66
|
|
67
|
+
def watched_tubes
|
68
|
+
watched_tube_names.map do |name|
|
69
|
+
HydroponicBean.tubes[name]
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
26
73
|
def create_job(pri, delay, ttr, data)
|
27
74
|
job = Job.new(current_tube, pri, delay, ttr, data)
|
28
75
|
|
29
76
|
HydroponicBean.jobs.push(job)
|
30
77
|
|
31
|
-
return job
|
78
|
+
return job
|
79
|
+
end
|
80
|
+
|
81
|
+
def deadline_soon?
|
82
|
+
HydroponicBean.update_time!
|
83
|
+
watched_tubes.map(&:reserved_jobs).flatten.select do |job|
|
84
|
+
job.reserved_by == self
|
85
|
+
end.sort_by(&:ttr_left).first&.deadline_soon?
|
86
|
+
end
|
87
|
+
|
88
|
+
def reserve_job
|
89
|
+
HydroponicBean.update_time!
|
90
|
+
reservable_jobs.first&.reserve(self)
|
91
|
+
end
|
92
|
+
|
93
|
+
def reservable_jobs
|
94
|
+
watched_tubes.reject(&:paused?).map(&:ready_jobs).flatten.sort_by(&:created_at).sort_by(&:pri)
|
95
|
+
end
|
96
|
+
|
97
|
+
def wait_for_job(timeout)
|
98
|
+
self.waiting = true
|
99
|
+
if timeout == 0
|
100
|
+
return reserve_job
|
101
|
+
else
|
102
|
+
timeout = [0, timeout].max
|
103
|
+
Timeout.timeout(timeout) do
|
104
|
+
while !(job = reserve_job)
|
105
|
+
sleep 0.49
|
106
|
+
end
|
107
|
+
return job
|
108
|
+
end
|
109
|
+
end
|
110
|
+
rescue Timeout::Error
|
111
|
+
return nil
|
112
|
+
ensure
|
113
|
+
self.waiting = false
|
114
|
+
end
|
115
|
+
|
116
|
+
def stats
|
117
|
+
{
|
118
|
+
'current-jobs-urgent' => HydroponicBean.jobs.select(&:urgent?).count,
|
119
|
+
'current-jobs-ready' => HydroponicBean.jobs.select(&:ready?).count,
|
120
|
+
'current-jobs-reserved' => HydroponicBean.jobs.select(&:reserved?).count,
|
121
|
+
'current-jobs-delayed' => HydroponicBean.jobs.select(&:delayed?).count,
|
122
|
+
'current-jobs-buried' => HydroponicBean.jobs.select(&:buried?).count,
|
123
|
+
'total-jobs' => HydroponicBean.jobs.count,
|
124
|
+
'current-tubes' => HydroponicBean.tubes.count,
|
125
|
+
'current-connections' => HydroponicBean.connections.count,
|
126
|
+
'current-producers' => HydroponicBean.connections.select(&:produced?).count,
|
127
|
+
'current-workers' => HydroponicBean.connections.select(&:workers?).count,
|
128
|
+
'current-waiting' => HydroponicBean.connections.select(&:waiting?).count,
|
129
|
+
}.merge(Hash[HydroponicBean.commands.map{|k, v| ["cmd-#{k}", v]}])
|
32
130
|
end
|
33
131
|
end
|
34
132
|
end
|
data/lib/hydroponic_bean/job.rb
CHANGED
@@ -5,9 +5,7 @@ module HydroponicBean
|
|
5
5
|
end
|
6
6
|
|
7
7
|
attr_accessor :id, :pri, :delay, :ttr, :data, :created_at,
|
8
|
-
:state, :tube, :stats
|
9
|
-
|
10
|
-
attr_reader :deleted
|
8
|
+
:reserved_at, :reserved_by, :state, :tube, :stats
|
11
9
|
|
12
10
|
def initialize(tube, pri, delay, ttr, data)
|
13
11
|
@id = self.class.next_id
|
@@ -15,6 +13,7 @@ module HydroponicBean
|
|
15
13
|
@tube = tube
|
16
14
|
@pri = pri.to_i
|
17
15
|
@delay = delay.to_i
|
16
|
+
@reserved_at = nil
|
18
17
|
@ttr = ttr.to_i
|
19
18
|
@state = @delay > 0 ? State.delayed : State.ready
|
20
19
|
@data = data
|
@@ -30,6 +29,15 @@ module HydroponicBean
|
|
30
29
|
@tube.push(self)
|
31
30
|
end
|
32
31
|
|
32
|
+
def update_time!
|
33
|
+
if (delayed? || reserved?) && time_left == 0
|
34
|
+
if reserved?
|
35
|
+
stats['timeouts'] += 1
|
36
|
+
end
|
37
|
+
@state = State.ready
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
33
41
|
def age
|
34
42
|
(Time.now.utc - created_at).to_i
|
35
43
|
end
|
@@ -44,13 +52,54 @@ module HydroponicBean
|
|
44
52
|
def delayed?; exists? && state == State.delayed; end
|
45
53
|
def buried?; exists? && state == State.buried; end
|
46
54
|
|
47
|
-
def
|
48
|
-
|
49
|
-
|
55
|
+
def reserved_by?(connection)
|
56
|
+
reserved? && reserved_by == connection
|
57
|
+
end
|
58
|
+
|
59
|
+
def reserve(connection)
|
60
|
+
if ready?
|
61
|
+
stats['reserves'] += 1
|
62
|
+
@state = State.reserved
|
63
|
+
@reserved_by = connection
|
64
|
+
@reserved_at = Time.now.utc
|
65
|
+
# For convenience and one-liners
|
66
|
+
return self
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def release(connection, pri, delay)
|
71
|
+
if reserved_by?(connection)
|
72
|
+
stats['releases'] += 1
|
73
|
+
@pri = pri.to_i
|
74
|
+
@delay = delay.to_i
|
75
|
+
@reserved_at = nil
|
76
|
+
@reserved_by = nil
|
77
|
+
@state = @delay > 0 ? State.delayed : State.ready
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def bury(connection, pri)
|
82
|
+
if reserved_by?(connection)
|
83
|
+
stats['buries'] += 1
|
84
|
+
@pri = pri.to_i
|
85
|
+
@reserved_at = nil
|
86
|
+
@reserved_by = nil
|
87
|
+
@state = State.buried
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def delete(connection)
|
92
|
+
if exists? && (!reserved? || reserved_by?(connection))
|
93
|
+
@tube.job_deleted!
|
50
94
|
@deleted = true
|
51
95
|
end
|
52
96
|
end
|
53
|
-
|
97
|
+
|
98
|
+
def touch(connection)
|
99
|
+
if reserved_by?(connection)
|
100
|
+
@reserved_at = Time.now.utc
|
101
|
+
end
|
102
|
+
end
|
54
103
|
|
55
104
|
def kick
|
56
105
|
if buried? || delayed?
|
@@ -59,8 +108,16 @@ module HydroponicBean
|
|
59
108
|
end
|
60
109
|
end
|
61
110
|
|
111
|
+
def ttr_left
|
112
|
+
[ttr - (Time.now.utc - reserved_at).to_i, 0].max
|
113
|
+
end
|
114
|
+
|
62
115
|
def time_left
|
63
|
-
reserved? ?
|
116
|
+
reserved? ? ttr_left : [delay - age, 0].max
|
117
|
+
end
|
118
|
+
|
119
|
+
def deadline_soon?
|
120
|
+
ttr_left <= 1
|
64
121
|
end
|
65
122
|
|
66
123
|
def serialize_stats
|
@@ -77,6 +134,9 @@ module HydroponicBean
|
|
77
134
|
}.merge(stats)
|
78
135
|
end
|
79
136
|
|
137
|
+
def deleted?; @deleted; end
|
138
|
+
def exists?; !@deleted; end
|
139
|
+
|
80
140
|
module State
|
81
141
|
def self.ready; :ready; end
|
82
142
|
def self.reserved; :reserved; end
|