pampa_dispatcher 1.1.1
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 +7 -0
- data/lib/pampa_dispatcher.rb +219 -0
- metadata +145 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 854adb6af15723ba6c9a2f4ac5af8acc8a47f4ba
|
4
|
+
data.tar.gz: 11091e316eab1a12bfb30114269ea612693aa59a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a89cd5510431a901f9677bd35cceabcf20d281e0d514f1063cc83c514335a6998ae14109ea15078e455d61853ac63358bbc0ee86a7a637b5fe4faff5bc81ab82
|
7
|
+
data.tar.gz: 324445697c97efa5ed2089e43326a9363165a7d7db044929d07eacf9834cbf3c2526bfb56d7b77e1475196f058c350d92aa6cabab8ac895c3250b23b8e7a3bbc
|
@@ -0,0 +1,219 @@
|
|
1
|
+
require 'pampa_workers'
|
2
|
+
|
3
|
+
module BlackStack
|
4
|
+
|
5
|
+
class Dispatcher
|
6
|
+
attr_accessor :name
|
7
|
+
# database information
|
8
|
+
# :field_times, :field_start_time and :field_end_time maybe nil
|
9
|
+
attr_accessor :table
|
10
|
+
attr_accessor :field_primary_key
|
11
|
+
attr_accessor :field_id
|
12
|
+
attr_accessor :field_time
|
13
|
+
attr_accessor :field_times
|
14
|
+
attr_accessor :field_start_time
|
15
|
+
attr_accessor :field_end_time
|
16
|
+
# max number of records assigned to a worker that have not started (:start_time field is nil)
|
17
|
+
attr_accessor :queue_size
|
18
|
+
# max number of minutes that a job should take to process. if :end_time keep nil x minutes
|
19
|
+
# after :start_time, that's considered as the job has failed or interrumped
|
20
|
+
attr_accessor :max_job_duration_minutes
|
21
|
+
# max number of times that a record can start to process & fail (:start_time field is not nil,
|
22
|
+
# but :end_time field is still nil after :max_job_duration_minutes)
|
23
|
+
attr_accessor :max_try_times
|
24
|
+
# additional function to decide how many records are pending for processing
|
25
|
+
# it should returns an integer
|
26
|
+
# keep it nil if you want to run the default function
|
27
|
+
attr_accessor :ocuppied_function
|
28
|
+
# additional function to decide if the worker can dispatch or not
|
29
|
+
# example: use this function when you want to decide based on the remaining credits of the client
|
30
|
+
# it should returns true or false
|
31
|
+
# keep it nil if you want it returns always true
|
32
|
+
attr_accessor :allowing_function
|
33
|
+
# additional function to choose the records to launch
|
34
|
+
# it should returns an array of IDs
|
35
|
+
# keep this parameter nil if you want to use the default algorithm
|
36
|
+
attr_accessor :selecting_function
|
37
|
+
# additional function to choose the records to retry
|
38
|
+
# keep this parameter nil if you want to use the default algorithm
|
39
|
+
attr_accessor :relaunching_function
|
40
|
+
# additional function to perform the update on a record to retry
|
41
|
+
# keep this parameter nil if you want to use the default algorithm
|
42
|
+
attr_accessor :relauncher_function
|
43
|
+
# additional function to perform the update on a record to flag the starting of the job
|
44
|
+
# by default this function will set the :field_start_time field with the current datetime, and it will increase the :field_times counter
|
45
|
+
# keep this parameter nil if you want to use the default algorithm
|
46
|
+
attr_accessor :starter_function
|
47
|
+
# additional function to perform the update on a record to flag the finishing of the job
|
48
|
+
# by default this function will set the :field_end_time field with the current datetime
|
49
|
+
# keep this parameter nil if you want to use the default algorithm
|
50
|
+
attr_accessor :finisher_function
|
51
|
+
|
52
|
+
|
53
|
+
# setup dispatcher configuration here
|
54
|
+
def initialize(h)
|
55
|
+
self.name = h[:name]
|
56
|
+
self.table = h[:table]
|
57
|
+
self.field_primary_key = h[:field_primary_key]
|
58
|
+
self.field_id = h[:field_id]
|
59
|
+
self.field_time = h[:field_time]
|
60
|
+
self.field_times = h[:field_times]
|
61
|
+
self.field_start_time = h[:field_start_time]
|
62
|
+
self.field_end_time = h[:field_end_time]
|
63
|
+
self.queue_size = h[:queue_size]
|
64
|
+
self.max_job_duration_minutes = h[:max_job_duration_minutes]
|
65
|
+
self.max_try_times = h[:max_try_times]
|
66
|
+
self.ocuppied_function = h[:ocuppied_function]
|
67
|
+
self.allowing_function = h[:allowing_function]
|
68
|
+
self.selecting_function = h[:selecting_function]
|
69
|
+
self.relaunching_function = h[:relaunching_function]
|
70
|
+
self.relauncher_function = h[:relauncher_function]
|
71
|
+
end
|
72
|
+
|
73
|
+
# decide how many records are pending for processing
|
74
|
+
# it will count the number if records with :reservation_id == worker.id, and :start_time == nil
|
75
|
+
# it returns an integer
|
76
|
+
def ocuppied_slots(worker)
|
77
|
+
if self.ocuppied_function.nil?
|
78
|
+
return self.table.where(self.field_id.to_sym => worker.id, self.field_start_time.to_sym => nil).count if !self.field_start_time.nil?
|
79
|
+
return self.table.where(self.field_id.to_sym => worker.id).count if self.field_start_time.nil?
|
80
|
+
else
|
81
|
+
# TODO: validar que retorna un entero
|
82
|
+
return self.ocuppied_function.call(worker, self)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# returns the number of free slots in the procesing queue of this worker
|
87
|
+
def available_slots(worker)
|
88
|
+
ocuppied = self.ocuppied_slots(worker)
|
89
|
+
allowed = self.queue_size
|
90
|
+
if ocuppied > allowed
|
91
|
+
return 0
|
92
|
+
else
|
93
|
+
return allowed - ocuppied
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# decide if the worker can dispatch or not
|
98
|
+
# example: use this function when you want to decide based on the remaining credits of the client
|
99
|
+
# returns always true
|
100
|
+
def allowing(worker)
|
101
|
+
if self.allowing_function.nil?
|
102
|
+
return true
|
103
|
+
else
|
104
|
+
# TODO: validar que retorna true o false
|
105
|
+
return self.allowing_function.call(worker, self)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# choose the records to dispatch
|
110
|
+
# returns an array of IDs
|
111
|
+
def selecting_dataset(worker, n)
|
112
|
+
ds = self.table.select(self.field_primary_key.to_sym).where(self.field_id.to_sym => nil)
|
113
|
+
ds = ds.filter(self.field_end_time.to_sym => nil) if !self.field_end_time.nil?
|
114
|
+
ds = ds.filter("#{self.field_times.to_s} IS NULL OR #{self.field_times.to_s} < #{self.max_try_times.to_s}") if !self.field_times.nil?
|
115
|
+
ds.limit(n)
|
116
|
+
end # selecting_dataset
|
117
|
+
|
118
|
+
def selecting(worker, n)
|
119
|
+
if self.allowing_function.nil?
|
120
|
+
return self.selecting_dataset(worker, n).map { |o| o[self.field_primary_key.to_sym] }
|
121
|
+
else
|
122
|
+
# TODO: validar que retorna un array de strings
|
123
|
+
return self.selecting_function.call(worker, self, n)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
# choose the records to retry
|
128
|
+
# returns an array of IDs
|
129
|
+
def relaunching_dataset(worker, n)
|
130
|
+
ds = self.table.select(self.field_primary_key.to_sym).where("#{self.field_time.to_s} < '#{(Time.now - 60*self.max_job_duration_minutes.to_i).strftime('%Y-%m-%d %H:%M:%S').to_s}'")
|
131
|
+
ds = ds.filter("#{self.field_end_time.to_s} IS NULL") if !self.field_end_time.nil?
|
132
|
+
# ds = ds.filter("( #{self.field_times.to_s} IS NULL OR #{self.field_times.to_s} < #{self.max_try_times.to_s} ) ") if !self.field_times.nil?
|
133
|
+
ds = ds.limit(n)
|
134
|
+
end
|
135
|
+
|
136
|
+
def relaunching(worker, n)
|
137
|
+
if self.relaunching_function.nil?
|
138
|
+
return self.relaunching_dataset(worker, n).map { |o| o[self.field_primary_key.to_sym] }
|
139
|
+
else
|
140
|
+
# TODO: validar que retorna un array de strings
|
141
|
+
return self.relaunching_function.call(worker, self, n)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def relaunch(o)
|
146
|
+
o[self.field_id.to_sym] = nil
|
147
|
+
o[self.field_time.to_sym] = nil
|
148
|
+
o[self.field_start_time.to_sym] = nil if !self.field_start_time.nil?
|
149
|
+
o[self.field_end_time.to_sym] = nil if !self.field_end_time.nil?
|
150
|
+
o.save
|
151
|
+
end
|
152
|
+
|
153
|
+
def start(o)
|
154
|
+
if self.starter_function.nil?
|
155
|
+
o[self.field_start_time.to_sym] = now() if !self.field_start_time.nil?
|
156
|
+
o[self.field_times.to_sym] = o[self.field_times.to_sym].to_i + 1 if !self.field_times.nil?
|
157
|
+
o.save
|
158
|
+
else
|
159
|
+
self.starter_function.call(o, self)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
def finish(o)
|
164
|
+
if self.finisher_function.nil?
|
165
|
+
o[self.field_end_time.to_sym] = now() if !self.field_end_time.nil?
|
166
|
+
o.save
|
167
|
+
else
|
168
|
+
self.finisher_function.call(o, self)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
# relaunch records
|
173
|
+
def run_relaunch(worker)
|
174
|
+
# relaunch failed records
|
175
|
+
self.relaunching(worker, self.queue_size).each { |id|
|
176
|
+
o = self.table.where(self.field_primary_key.to_sym => id).first
|
177
|
+
if self.relauncher_function.nil?
|
178
|
+
self.relaunch(o)
|
179
|
+
else
|
180
|
+
self.relauncher_function.call(o, self)
|
181
|
+
end
|
182
|
+
# release resources
|
183
|
+
DB.disconnect
|
184
|
+
GC.start
|
185
|
+
}
|
186
|
+
end # def run_relaunch
|
187
|
+
|
188
|
+
# dispatch records
|
189
|
+
# returns the # of records dispatched
|
190
|
+
def run_dispatch(worker)
|
191
|
+
# get # of available slots
|
192
|
+
n = self.available_slots(worker)
|
193
|
+
|
194
|
+
# dispatching n pending records
|
195
|
+
i = 0
|
196
|
+
if n>0
|
197
|
+
self.selecting(worker, n).each { |id|
|
198
|
+
# count the # of dispatched
|
199
|
+
i += 1
|
200
|
+
# dispatch records
|
201
|
+
o = self.table.where(self.field_primary_key.to_sym => id).first
|
202
|
+
o[self.field_id.to_sym] = worker.id
|
203
|
+
o[self.field_time.to_sym] = now()
|
204
|
+
o[self.field_start_time.to_sym] = nil if !self.field_start_time.nil?
|
205
|
+
o[self.field_end_time.to_sym] = nil if !self.field_end_time.nil?
|
206
|
+
o.save
|
207
|
+
# release resources
|
208
|
+
DB.disconnect
|
209
|
+
GC.start
|
210
|
+
}
|
211
|
+
end
|
212
|
+
|
213
|
+
#
|
214
|
+
return i
|
215
|
+
end
|
216
|
+
|
217
|
+
end # class Dispatcher
|
218
|
+
|
219
|
+
end # module BlackStack
|
metadata
ADDED
@@ -0,0 +1,145 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: pampa_dispatcher
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Leandro Daniel Sardi
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-01-02 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: websocket
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 1.2.8
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 1.2.8
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - "~>"
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 1.2.8
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 1.2.8
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: json
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: 1.8.1
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: 1.8.1
|
43
|
+
type: :runtime
|
44
|
+
prerelease: false
|
45
|
+
version_requirements: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - "~>"
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: 1.8.1
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: 1.8.1
|
53
|
+
- !ruby/object:Gem::Dependency
|
54
|
+
name: tiny_tds
|
55
|
+
requirement: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - "~>"
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: 1.0.5
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: 1.0.5
|
63
|
+
type: :runtime
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - "~>"
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 1.0.5
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: 1.0.5
|
73
|
+
- !ruby/object:Gem::Dependency
|
74
|
+
name: sequel
|
75
|
+
requirement: !ruby/object:Gem::Requirement
|
76
|
+
requirements:
|
77
|
+
- - "~>"
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: 4.28.0
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 4.28.0
|
83
|
+
type: :runtime
|
84
|
+
prerelease: false
|
85
|
+
version_requirements: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 4.28.0
|
90
|
+
- - ">="
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
version: 4.28.0
|
93
|
+
- !ruby/object:Gem::Dependency
|
94
|
+
name: pampa_workers
|
95
|
+
requirement: !ruby/object:Gem::Requirement
|
96
|
+
requirements:
|
97
|
+
- - "~>"
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: 1.1.1
|
100
|
+
- - ">="
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: 1.1.1
|
103
|
+
type: :runtime
|
104
|
+
prerelease: false
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - "~>"
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: 1.1.1
|
110
|
+
- - ">="
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
version: 1.1.1
|
113
|
+
description: 'THIS GEM IS STILL IN DEVELOPMENT STAGE. Find documentation here: https://github.com/leandrosardi/pampa_dispatcher.'
|
114
|
+
email: leandro.sardi@expandedventure.com
|
115
|
+
executables: []
|
116
|
+
extensions: []
|
117
|
+
extra_rdoc_files: []
|
118
|
+
files:
|
119
|
+
- lib/pampa_dispatcher.rb
|
120
|
+
homepage: https://rubygems.org/gems/pampa_dispatcher
|
121
|
+
licenses:
|
122
|
+
- MIT
|
123
|
+
metadata: {}
|
124
|
+
post_install_message:
|
125
|
+
rdoc_options: []
|
126
|
+
require_paths:
|
127
|
+
- lib
|
128
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
129
|
+
requirements:
|
130
|
+
- - ">="
|
131
|
+
- !ruby/object:Gem::Version
|
132
|
+
version: '0'
|
133
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
134
|
+
requirements:
|
135
|
+
- - ">="
|
136
|
+
- !ruby/object:Gem::Version
|
137
|
+
version: '0'
|
138
|
+
requirements: []
|
139
|
+
rubyforge_project:
|
140
|
+
rubygems_version: 2.4.5.1
|
141
|
+
signing_key:
|
142
|
+
specification_version: 4
|
143
|
+
summary: THIS GEM IS STILL IN DEVELOPMENT STAGE. Distribute work along a pool of Pampa
|
144
|
+
workers.
|
145
|
+
test_files: []
|