pampa 2.0.28 → 2.0.30

Sign up to get free protection for your applications and to get access to all the features.
data/lib/pampa.rb CHANGED
@@ -1,104 +1,21 @@
1
- require 'sequel'
2
1
  require 'blackstack-core'
2
+ require 'blackstack-db'
3
3
  require 'blackstack-nodes'
4
- require 'blackstack-deployer'
5
4
  require 'simple_command_line_parser'
6
5
  require 'simple_cloud_logging'
6
+ require 'colorize'
7
+ require 'sinatra'
7
8
 
8
9
  module BlackStack
9
10
  module Pampa
10
- # activate this flag if you want to add pampa nodes to blackstack-deployer.
11
- @@integrate_with_blackstack_deployer = false
12
- # setup custom locations for config and worker files.
13
- @@config_filename = "config.rb"
14
- @@worker_filename = "worker.rb"
15
- # setu the directory where the worker.rb file will be lauched, and the log files will be stored.
16
- @@working_directory = "$HOME/pampa"
17
11
  # arrays of workers, nodes, and jobs.
18
12
  @@nodes = []
19
13
  @@jobs = []
20
- # logger configuration
21
- @@log_filename = nil
22
14
  @@logger = BlackStack::DummyLogger.new(nil)
23
- # Connection string to the database. Example: mysql2://user:password@localhost:3306/database
24
- @@connection_string = nil
25
-
26
- def self.now()
27
- tz = 'America/Argentina/Buenos_Aires' #DB["SELECT current_setting('TIMEZONE') AS tz"].first[:tz]
28
- DB["SELECT current_timestamp at TIME ZONE '#{tz}' AS now"].first[:now]
29
- end
30
-
31
- # @@integrate_with_blackstack_deployer
32
- def self.integrate_with_blackstack_deployer()
33
- @@integrate_with_blackstack_deployer
34
- end
35
-
36
- def self.set_integrate_with_blackstack_deployer(b)
37
- @@integrate_with_blackstack_deployer = b
38
- end
39
-
40
- # @@config_filename
41
- def self.config_filename()
42
- @@config_filename
43
- end
44
-
45
- def self.set_config_filename(s)
46
- @@config_filename = s
47
- end
48
-
49
- # @@worker_filename
50
- def self.worker_filename()
51
- @@worker_filename
52
- end
53
-
54
- def self.set_worker_filename(s)
55
- @@worker_filename = s
56
- end
57
-
58
- ## @@working_directory
59
- def self.working_directory()
60
- @@working_directory
61
- end
62
-
63
- def self.set_working_directory(s)
64
- @@working_directory = s
65
- end
66
-
67
- # define a filename for the log file.
68
- def self.set_log_filename(s)
69
- @@log_filename = s
70
- @@logger = BlackStack::LocalLogger.new(s)
71
- end
72
-
73
- # return the logger.
74
- def self.logger()
75
- @@logger
76
- end
77
-
78
- def self.set_logger(l)
79
- @@logger = l
80
- end
81
-
82
- # return the log filename.
83
- def self.log_filename()
84
- @@log_filename
85
- end
86
-
87
- # define a connection string to the database.
88
- def self.set_connection_string(s)
89
- @@connection_string = s
90
- end
91
-
92
- # return connection string to the database. Example: mysql2://user:password@localhost:3306/database
93
- def self.connection_string()
94
- @@connection_string
95
- end
96
15
 
97
16
  # add a node to the cluster.
98
17
  def self.add_node(h)
99
18
  @@nodes << BlackStack::Pampa::Node.new(h)
100
- # add to deployer
101
- BlackStack::Deployer.add_node(h) if @@integrate_with_blackstack_deployer
102
19
  end # def self.add_node(h)
103
20
 
104
21
  # add an array of nodes to the cluster.
@@ -119,7 +36,7 @@ module BlackStack
119
36
 
120
37
  # return the array of all workers, beloning all nodes.
121
38
  def self.workers()
122
- @@nodes.map { |node| node.workers }.flatten
39
+ @@nodes.map { |node| node.workers }.flatten
123
40
  end
124
41
 
125
42
  # add a job to the cluster.
@@ -143,6 +60,15 @@ module BlackStack
143
60
  @@jobs
144
61
  end
145
62
 
63
+ # get and set logger
64
+ def self.logger()
65
+ @@logger
66
+ end
67
+
68
+ def self.set_logger(l)
69
+ @@logger = l
70
+ end
71
+
146
72
  # get attached and unassigned workers.
147
73
  # assign and unassign workers to jobs.
148
74
  #
@@ -151,70 +77,63 @@ module BlackStack
151
77
  # - worker: relative path of the worker.rb file. Example: '../worker.rb'
152
78
  #
153
79
  def self.stretch()
154
- # validate: the connection string is not nil
155
- raise "The connection string is nil" if @@connection_string.nil?
156
- # validate: the connection string is not empty
157
- raise "The connection string is empty" if @@connection_string.empty?
158
- # validate: the connection string is not blank
159
- raise "The connection string is blank" if @@connection_string.strip.empty?
160
80
  # getting logger
161
81
  l = self.logger()
162
82
  # get the job this worker is working with
163
83
  BlackStack::Pampa.jobs.each { |job|
164
- l.log ''
165
84
  l.logs "job #{job.name}... "
166
85
  # get attached and unassigned workers
167
86
  l.logs "Getting attached and unassigned workers... "
168
87
  workers = BlackStack::Pampa.workers.select { |w| w.attached && w.assigned_job.nil? }
169
- l.logf "done (#{workers.size.to_s})"
88
+ l.logf 'done'.green + " (#{workers.size.to_s.blue})"
170
89
  # get the workers that match the filter
171
90
  l.logs "Getting workers that match the filter... "
172
91
  workers = workers.select { |w| w.id =~ job.filter_worker_id }
173
- l.logf "done (#{workers.size.to_s})"
92
+ l.logf "done".green + " (#{workers.size.to_s.blue})"
174
93
  # if theere are workers
175
94
  if workers.size > 0
176
95
  l.logs("Gettting assigned workers... ")
177
96
  assigned = BlackStack::Pampa.workers.select { |worker| worker.attached && worker.assigned_job.to_s == job.name.to_s }
178
- l.logf("done (#{assigned.size.to_s})")
97
+ l.logf "done ".green + " (#{assigned.size.to_s.blue})"
179
98
 
180
99
  l.logs("Getting total pending (pending) tasks... ")
181
100
  pendings = job.pending
182
- l.logf("done (#{pendings.to_s})")
101
+ l.logf "done".green + " (#{pendings.to_s.blue})"
183
102
 
184
103
  l.logs("0 pending tasks?.... ")
185
104
  if pendings.size == 0
186
- l.logf("yes")
105
+ l.logf "yes".green
187
106
 
188
107
  l.logs("Unassigning all assigned workers... ")
189
108
  assigned.each { |w|
190
109
  l.logs("Unassigning worker... ")
191
110
  w.assigned_job = nil
192
111
  workers << w # add worker back to the list of unassigned
193
- l.logf "done (#{w.id})"
112
+ l.logf "done".green + " (#{w.id.to_s.blue})"
194
113
  }
195
114
  l.done
196
115
  else
197
- l.logf("no")
116
+ l.logf "no".red
198
117
 
199
118
  l.logs("Under :max_pending_tasks (#{job.max_pending_tasks}) and more than 1 assigned workers ?... ")
200
119
  if pendings.size < job.max_pending_tasks && assigned.size > 1
201
- l.logf("yes")
120
+ l.logf "yes".green
202
121
 
203
122
  while assigned.size > 1
204
123
  l.logs("Unassigning worker... ")
205
124
  w = assigned.pop # TODO: find a worker with no pending tasks
206
125
  w.assigned_job = nil
207
126
  workers << w # add worker back to the array of unassigned workers
208
- l.logf "done (#{w.id})"
127
+ l.logf "done".green + " (#{w.id.to_s.blue})"
209
128
  end
210
129
  else
211
- l.logf("no")
130
+ l.logf "no".red
212
131
 
213
- l.logs("Over :max_assigned_workers (#{job.max_assigned_workers}) and more than 1 assigned workers?... ")
132
+ l.logs("Over :max_assigned_workers (#{job.max_assigned_workers.to_s.blue}) and more than 1 assigned workers?... ")
214
133
  if assigned.size >= job.max_assigned_workers && assigned.size > 1
215
- l.logf("yes")
134
+ l.logf("yes".green)
216
135
  else
217
- l.logf("no")
136
+ l.logf("no".red)
218
137
 
219
138
  i = assigned.size
220
139
  while i < job.max_assigned_workers
@@ -222,11 +141,11 @@ module BlackStack
222
141
  l.logs("Assigning worker... ")
223
142
  w = workers.pop
224
143
  if w.nil?
225
- l.logf("no more workers")
144
+ l.logf("no more workers".yellow)
226
145
  break
227
146
  else
228
147
  w.assigned_job = job.name.to_sym
229
- l.logf "done (#{w.id})"
148
+ l.logf "done".green + " (#{w.id.to_s.blue})"
230
149
  end
231
150
  end # while i < job.max_assigned_workers
232
151
  end # if assigned.size >= job.max_assigned_workers && assigned.size > 0
@@ -235,7 +154,7 @@ module BlackStack
235
154
  end # if workers.size > 0
236
155
  l.done
237
156
  }
238
- end
157
+ end # def self.stretch()
239
158
 
240
159
  # iterate the jobs.
241
160
  # for each job, get all the tasks to relaunch.
@@ -246,12 +165,6 @@ module BlackStack
246
165
  # - worker: relative path of the worker.rb file. Example: '../worker.rb'
247
166
  #
248
167
  def self.relaunch(n=10000)
249
- # validate: the connection string is not nil
250
- raise "The connection string is nil" if @@connection_string.nil?
251
- # validate: the connection string is not empty
252
- raise "The connection string is empty" if @@connection_string.empty?
253
- # validate: the connection string is not blank
254
- raise "The connection string is blank" if @@connection_string.strip.empty?
255
168
  # getting logger
256
169
  l = self.logger()
257
170
  # iterate the workers
@@ -259,7 +172,7 @@ module BlackStack
259
172
  l.logs("job:#{job.name}... ")
260
173
  l.logs("Gettting tasks to relaunch (max #{n})... ")
261
174
  tasks = job.relaunching(n)
262
- l.logf("done (#{tasks.size.to_s})")
175
+ l.logf("done".green + " (#{tasks.size.to_s.blue})")
263
176
 
264
177
  tasks.each { |task|
265
178
  l.logs("Relaunching task #{task[job.field_primary_key.to_sym]}... ")
@@ -269,7 +182,7 @@ module BlackStack
269
182
 
270
183
  l.done
271
184
  }
272
- end
185
+ end # def self.relaunch(n=10000)
273
186
 
274
187
  # iterate the workers.
275
188
  # for each worker, iterate the job.
@@ -279,245 +192,28 @@ module BlackStack
279
192
  # - worker: relative path of the worker.rb file. Example: '../worker.rb'
280
193
  #
281
194
  def self.dispatch()
282
- # validate: the connection string is not nil
283
- raise "The connection string is nil" if @@connection_string.nil?
284
- # validate: the connection string is not empty
285
- raise "The connection string is empty" if @@connection_string.empty?
286
- # validate: the connection string is not blank
287
- raise "The connection string is blank" if @@connection_string.strip.empty?
288
195
  # getting logger
289
196
  l = self.logger()
290
197
  # iterate the workers
291
198
  BlackStack::Pampa.workers.each { |worker|
292
199
  l.logs("worker:#{worker.id} (job:#{worker.assigned_job.to_s})... ")
293
200
  if !worker.attached
294
- l.logf("detached")
201
+ l.logf("detached".green)
295
202
  else
296
203
  if worker.assigned_job.nil?
297
- l.logf("unassigned")
204
+ l.logf("unassigned".yellow)
298
205
  else
299
206
  # get the job this worker is assigned to
300
207
  job = BlackStack::Pampa.jobs.select { |j| j.name.to_s == worker.assigned_job.to_s }.first
301
208
  if job.nil?
302
- l.logf("job #{job.name} not found")
209
+ l.logf("job #{job.name} not found".red)
303
210
  else
304
- l.logf("done (#{job.run_dispatch(worker).to_s})")
211
+ l.logf("done".green + " (#{job.run_dispatch(worker).to_s.blue})")
305
212
  end
306
213
  end
307
214
  end
308
215
  } # @@nodes.each do |node|
309
- end
310
-
311
- # connect the nodes via ssh.
312
- # kill all Ruby processes except this one.
313
- # rename any existing folder $HOME/pampa to $HOME/pampa.<current timestamp>.
314
- # create a new folder $HOME/pampa.
315
- # build the file $HOME/pampa/config.rb in the remote node.
316
- # copy the file $HOME/pampa/worker.rb to the remote node.
317
- # run the number of workers specified in the configuration of the Pampa module.
318
- # return an array with the IDs of the workers.
319
- #
320
- # Parameters:
321
- # - config: relative path of the configuration file. Example: '../config.rb'
322
- # - worker: relative path of the worker.rb file. Example: '../worker.rb'
323
- #
324
- def self.deploy()
325
- # validate: the connection string is not nil
326
- raise "The connection string is nil" if @@connection_string.nil?
327
- # validate: the connection string is not empty
328
- raise "The connection string is empty" if @@connection_string.empty?
329
- # validate: the connection string is not blank
330
- raise "The connection string is blank" if @@connection_string.strip.empty?
331
- # getting logger
332
- l = self.logger()
333
- # iterate the nodes
334
- @@nodes.each { |node|
335
- l.logs("node:#{node.name()}... ")
336
- # connect the node
337
- l.logs("Connecting... ")
338
- node.connect()
339
- l.done
340
- # kill all ruby processes except this one
341
- l.logs("Killing all Ruby processes except this one... ")
342
- node.kill_workers()
343
- l.done
344
- # rename any existing folder ~/code/pampa to ~/code/pampa.<current timestamp>.
345
- l.logs("Renaming old folder... ")
346
- node.exec("mv #{BlackStack::Pampa.working_directory} #{BlackStack::Pampa.working_directory}.#{Time.now().to_i.to_s}", false);
347
- l.done
348
- # create a new folder ~/code. - ignore if it already exists.
349
- l.logs("Creating new folder... ")
350
- node.exec("mkdir #{BlackStack::Pampa.working_directory}", false);
351
- l.done
352
- # build the file $HOME/pampa/config.rb in the remote node. - Be sure the BlackStack::Pampa.to_hash.to_s don't have single-quotes (') in the string.
353
- l.logs("Building config file... ")
354
- s = "echo \"#{File.read(config_filename)}\" > #{BlackStack::Pampa.working_directory}/#{BlackStack::Pampa.config_filename}"
355
- node.exec("#{s}", false);
356
- l.done
357
- # copy the file $HOME/pampa/worker.rb to the remote node. - Be sure the script don't have single-quotes (') in the string.
358
- l.logs("Copying worker file... ")
359
- s = "echo \"#{File.read(worker_filename)}\" > #{BlackStack::Pampa.working_directory}/#{BlackStack::Pampa.worker_filename}"
360
- node.exec("#{s}", false);
361
- l.done
362
- # run the number of workers specified in the configuration of the Pampa module.
363
- node.workers.each { |worker|
364
- # run the worker
365
- # add these parameters for debug: debug=yes pampa=~/code/pampa/lib/pampa.rb
366
- l.logs "Running worker #{worker.id}... "
367
-
368
- # write bash command to initialize bash file
369
- s = "echo \"
370
- export RUBYLIB=$HOME/code/mysaas;
371
- source $HOME/.profile;
372
- source /usr/local/rvm/scripts/rvm;
373
- cd ~/code/mysaas; rvm install 3.1.2;
374
- rvm --default use 3.1.2;
375
- cd #{BlackStack::Pampa.working_directory};
376
- nohup ruby #{worker_filename} id=#{worker.id} config=#{self.config_filename} >/dev/null 2>&1 &
377
- \" > #{BlackStack::Pampa.working_directory}/#{worker.id}.sh"
378
- node.exec(s, false);
379
-
380
- #s = "nohup bash #{BlackStack::Pampa.working_directory}/worker.sh >/dev/null 2>&1 &"
381
- s = "bash #{BlackStack::Pampa.working_directory}/#{worker.id}.sh"
382
- node.exec(s, false);
383
-
384
- l.done
385
- }
386
- # disconnect the node
387
- l.logs("Disconnecting... ")
388
- node.disconnect()
389
- l.done
390
- l.done
391
- } # @@nodes.each do |node|
392
- end
393
-
394
- # connect the nodes via ssh.
395
- # kill all Ruby processes except this one.
396
- # run the number of workers specified in the configuration of the Pampa module.
397
- # return an array with the IDs of the workers.
398
- #
399
- def self.start()
400
- # validate: the connection string is not nil
401
- raise "The connection string is nil" if @@connection_string.nil?
402
- # validate: the connection string is not empty
403
- raise "The connection string is empty" if @@connection_string.empty?
404
- # validate: the connection string is not blank
405
- raise "The connection string is blank" if @@connection_string.strip.empty?
406
- # getting logger
407
- l = self.logger()
408
- # iterate the nodes
409
- @@nodes.each { |node|
410
- l.logs("node:#{node.name()}... ")
411
- # connect the node
412
- l.logs("Connecting... ")
413
- node.connect()
414
- l.done
415
- # kill all ruby processes except this one
416
- l.logs("Killing all Ruby processes except this one... ")
417
- node.kill_workers()
418
- l.done
419
- # run the number of workers specified in the configuration of the Pampa module.
420
- node.workers.each { |worker|
421
- # run the worker
422
- # add these parameters for debug: debug=yes pampa=~/code/pampa/lib/pampa.rb
423
- # run a bash command that sources the .profile file and runs the ruby script in the background, returning immediatelly.
424
-
425
- l.logs "Running worker #{worker.id}... "
426
-
427
- # write bash command to initialize bash file
428
- s = "echo \"
429
- export RUBYLIB=$HOME/code/mysaas;
430
- source $HOME/.profile;
431
- source /usr/local/rvm/scripts/rvm;
432
- cd ~/code/mysaas; rvm install 3.1.2;
433
- rvm --default use 3.1.2;
434
- cd #{BlackStack::Pampa.working_directory};
435
- nohup ruby #{worker_filename} id=#{worker.id} config=#{self.config_filename} >/dev/null 2>&1 &
436
- \" > #{BlackStack::Pampa.working_directory}/#{worker.id}.sh"
437
- node.exec(s, false);
438
- s = "nohup bash #{BlackStack::Pampa.working_directory}/#{worker.id}.sh >/dev/null 2>&1 &"
439
- node.exec(s, false);
440
-
441
- l.done
442
- }
443
- # disconnect the node
444
- l.logs("Disconnecting... ")
445
- node.disconnect()
446
- l.done
447
- l.done
448
- } # @@nodes.each do |node|
449
- end
450
-
451
- # connect the nodes via ssh.
452
- # kill all Ruby processes except this one.
453
- #
454
- # Parameters:
455
- # - config: relative path of the configuration file. Example: '../config.rb'
456
- #
457
- def self.stop()
458
- # validate: the connection string is not nil
459
- raise "The connection string is nil" if @@connection_string.nil?
460
- # validate: the connection string is not empty
461
- raise "The connection string is empty" if @@connection_string.empty?
462
- # validate: the connection string is not blank
463
- raise "The connection string is blank" if @@connection_string.strip.empty?
464
- # getting logger
465
- l = self.logger()
466
- # iterate the nodes
467
- @@nodes.each { |node|
468
- l.logs("node:#{node.name()}... ")
469
- # connect the node
470
- l.logs("Connecting... ")
471
- node.connect()
472
- l.done
473
- # kill all ruby processes except this one
474
- l.logs("Killing all Ruby processes except this one... ")
475
- node.kill_workers()
476
- l.done
477
- # disconnect the node
478
- l.logs("Disconnecting... ")
479
- node.disconnect()
480
- l.done
481
- l.done
482
- } # @@nodes.each do |node|
483
- end
484
-
485
- # get the node by `node_name`
486
- # connect the nodes via ssh.
487
- # get how many minutes the worker wrote the log file
488
- # close the connection
489
- #
490
- # DEPRECATED. Use `ps aux | grep "..."` to know if a process is running or not.
491
- #
492
- def self.log_minutes_ago(node_name, worker_id)
493
- # get the node
494
- n = self.nodes.select { |n| n.name == node_name }.first
495
- return nil if !n
496
- # connect the node
497
- n.connect()
498
- # get the time of the last time the worker wrote the log file
499
- code = "cat #{BlackStack::Pampa.working_directory}/worker.#{worker_id}.log | tail -n 1 | cut -b1-19"
500
- s = n.exec(code, false).to_s.strip
501
- # run bash command to get the difference in minutes beteen now and the last time the worker wrote the log file
502
- s = n.exec("echo \"$(($(date +%s) - $(date -d '#{s}' +%s))) / 60\" | bc", false).to_s.strip
503
- # disconnect the node
504
- n.disconnect
505
- # return the number of minutes
506
- s
507
- end # log_minutes_ago
508
-
509
- # get the node usage of CPU, RAM, DISK, and NETWORK
510
- # return a hash with the usage of CPU, RAM, DISK, and NETWORK
511
- #
512
- # sudo apt install sysstat
513
- #
514
- def self.node_usage(node_name)
515
- ret = {}
516
- # get the node
517
- n = self.nodes.select { |n| n.name == node_name }.first
518
- return nil if !n
519
- n.usage
520
- end # node_usage
216
+ end # def self.dispatch()
521
217
 
522
218
  # stub worker class
523
219
  class Worker
@@ -551,10 +247,6 @@ module BlackStack
551
247
  def detach()
552
248
  self.attached = false
553
249
  end
554
- # get the latest n lines of the log of this worker
555
- def tail(n=10)
556
- self.node.tail("#{BlackStack::Pampa.working_directory}/worker.#{self.id}.log", n)
557
- end
558
250
  end
559
251
 
560
252
  # stub node class
@@ -825,7 +517,7 @@ module BlackStack
825
517
  q = "
826
518
  SELECT *
827
519
  FROM #{self.table.to_s}
828
- WHERE COALESCE(#{self.field_time.to_s}, '1900-01-01') < CAST('#{BlackStack::Pampa.now}' AS TIMESTAMP) - INTERVAL '#{self.max_job_duration_minutes.to_i} minutes'
520
+ WHERE COALESCE(#{self.field_time.to_s}, '1900-01-01') < CAST('#{now}' AS TIMESTAMP) - INTERVAL '#{self.max_job_duration_minutes.to_i} minutes'
829
521
  AND #{self.field_id.to_s} IS NOT NULL
830
522
  AND #{self.field_end_time.to_s} IS NULL
831
523
  --AND COALESCE(#{self.field_times.to_s},0) < #{self.max_try_times.to_i}
@@ -870,7 +562,7 @@ module BlackStack
870
562
 
871
563
  def start(o)
872
564
  if self.starter_function.nil?
873
- o[self.field_start_time.to_sym] = DB["SELECT CAST('#{BlackStack::Pampa.now}' AS TIMESTAMP) AS dt"].first[:dt] if !self.field_start_time.nil? # IMPORTANT: use DB location to get current time.
565
+ o[self.field_start_time.to_sym] = DB["SELECT CAST('#{now}' AS TIMESTAMP) AS dt"].first[:dt] if !self.field_start_time.nil? # IMPORTANT: use DB location to get current time.
874
566
  o[self.field_times.to_sym] = o[self.field_times.to_sym].to_i + 1
875
567
  self.update(o)
876
568
  else
@@ -880,7 +572,7 @@ module BlackStack
880
572
 
881
573
  def finish(o, e=nil)
882
574
  if self.finisher_function.nil?
883
- o[self.field_end_time.to_sym] = DB["SELECT CAST('#{BlackStack::Pampa.now}' AS TIMESTAMP) AS dt"].first[:dt] if !self.field_end_time.nil? && e.nil? # IMPORTANT: use DB location to get current time.
575
+ o[self.field_end_time.to_sym] = DB["SELECT CAST('#{now}' AS TIMESTAMP) AS dt"].first[:dt] if !self.field_end_time.nil? && e.nil? # IMPORTANT: use DB location to get current time.
884
576
  o[self.field_success.to_sym] = e.nil?
885
577
  o[self.field_error_description.to_sym] = e.to_console if !e.nil?
886
578
  self.update(o)
@@ -913,7 +605,7 @@ module BlackStack
913
605
  # dispatching n pending records
914
606
  i = 0
915
607
  if n>0
916
- ids = self.selecting(n).map { |h| h[:id] }
608
+ ids = self.selecting(n).map { |h| h[self.field_primary_key.to_sym] }
917
609
 
918
610
  i = ids.size
919
611
 
@@ -937,7 +629,7 @@ module BlackStack
937
629
  end
938
630
 
939
631
  q += "
940
- #{self.field_time.to_s} = CAST('#{BlackStack::Pampa.now}' AS TIMESTAMP)
632
+ #{self.field_time.to_s} = CAST('#{now}' AS TIMESTAMP)
941
633
  WHERE #{self.field_primary_key.to_s} IN ('#{ids.join("','")}')
942
634
  "
943
635
 
@@ -1020,55 +712,7 @@ module BlackStack
1020
712
  else
1021
713
  return self.failed_function.call
1022
714
  end
1023
- end # def falsed
1024
-
1025
- # reporting method: timeline
1026
- # Return an array of hashes with the number of successfull processed taasks in the last period.
1027
- # The period is defined by the `scale_unit` and `scale_points` parameters.
1028
- # The `scale_unit` can be `minutes`, `hours`, `days`, `weeks`, `months`, `years`.
1029
- # The `scale_points` is the number of `scale_unit` to be reported, and it must be an integer higer than 0.
1030
- # if the numbr if running tasks is higher than `max_tasks_to_show` then it returns `max_tasks_to_show`+.
1031
- def timeline(scale_unit='minutes', scale_points=60)
1032
- j = self
1033
- a = []
1034
- # validate: The period is defined by the `scale_unit` and `scale_points` parameters.
1035
- if !['minutes', 'hours', 'days', 'weeks', 'months', 'years'].include?(scale_unit)
1036
- raise "Invalid scale_unit: #{scale_unit}"
1037
- end
1038
- # validate: The `scale_points` is the number of `scale_unit` to be reported, and it must be an integer higer than 0.
1039
- if !scale_points.is_a?(Integer) || scale_points<=0
1040
- raise "Invalid scale_points: #{scale_points}"
1041
- end
1042
- # generate report
1043
- point = 0
1044
- while point<scale_points
1045
- point += 1
1046
- q = "
1047
- SELECT COUNT(*) AS n
1048
- FROM #{j.table.to_s}
1049
- WHERE COALESCE(#{j.field_success.to_s},false)=true
1050
- AND #{j.field_time.to_s} >= CAST('#{BlackStack::Pampa.now - point.send(scale_unit)}' AS TIMESTAMP)
1051
- AND #{j.field_time.to_s} < CAST('#{BlackStack::Pampa.now - (point-1).send(scale_unit)}' AS TIMESTAMP)
1052
- "
1053
- a << { :time => BlackStack::Pampa.now - (point-1).send(scale_unit), :n => DB[q].first[:n].to_i }
1054
- end # while point<scale_points
1055
- # return
1056
- a
1057
- end # def timeline
1058
-
1059
- # reporting method: error_descriptions
1060
- # return an array of hashes { :id, :error_description } with the tasks that have an the success flag in false, error description.
1061
- # if the numbr if running tasks is higher than `max_tasks_to_show` then it returns `max_tasks_to_show` errors.
1062
- def error_descriptions(max_tasks_to_show=25)
1063
- j = self
1064
- q = "
1065
- SELECT #{j.field_primary_key.to_s} as id, #{j.field_error_description.to_s} as description
1066
- FROM #{j.table.to_s}
1067
- WHERE COALESCE(#{j.field_success.to_s},true)=false
1068
- LIMIT #{max_tasks_to_show}
1069
- "
1070
- DB[q].all
1071
- end
715
+ end # def failed
1072
716
  end # class Job
1073
717
  end # module Pampa
1074
718
  end # module BlackStack
data/pampa.gemspec ADDED
@@ -0,0 +1,43 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'pampa'
3
+ s.version = '2.0.30'
4
+ s.date = '2023-10-06'
5
+ s.summary = "Ruby library for async & distributed computing, supporting dynamic reconfiguration, distribution of the computation jobs, error handling, job-retry and fault tolerance, and fast (non-direct) communication to ensure real-time capabilities."
6
+ s.description = "Pampa is a Ruby library for async & distributing computing providing the following features:
7
+
8
+ - cluster-management with dynamic reconfiguration (joining and leaving nodes);
9
+ - distribution of the computation jobs to the (active) nodes;
10
+ - error handling, job-retry and fault tolerance;
11
+ - fast (non-direct) communication to ensure realtime capabilities.
12
+
13
+ The Pampa framework may be widely used for:
14
+
15
+ - large scale web scraping with what we call a \"bot-farm\";
16
+ - payments processing for large-scale ecommerce websites;
17
+ - reports generation for high demanded SaaS platforms;
18
+ - heavy mathematical model computing;
19
+
20
+ and any other tasks that requires a virtually infinite amount of CPU computing and memory resources.
21
+
22
+ Find documentation here: https://github.com/leandrosardi/pampa
23
+ "
24
+ s.authors = ["Leandro Daniel Sardi"]
25
+ s.email = 'leandro.sardi@expandedventure.com'
26
+ s.files = [
27
+ 'lib/pampa/worker.rb',
28
+ 'lib/pampa/dispatcher.rb',
29
+ 'lib/pampa/app.rb',
30
+ 'lib/pampa.rb',
31
+ 'pampa.gemspec'
32
+ ]
33
+ s.homepage = 'https://github.com/leandrosardi/pampa'
34
+ s.license = 'MIT'
35
+ s.add_runtime_dependency 'rubygems-bundler', '~> 1.4.5', '>= 1.4.5'
36
+ s.add_runtime_dependency 'sinatra', '~> 2.2.4', '>= 2.2.4'
37
+ s.add_runtime_dependency 'colorize', '~> 0.8.1', '>= 0.8.1'
38
+ s.add_runtime_dependency 'blackstack-core', '~> 1.2.15', '>= 1.2.15'
39
+ s.add_runtime_dependency 'blackstack-db', '~> 1.0.1', '>= 1.0.1'
40
+ s.add_runtime_dependency 'blackstack-nodes', '~> 1.2.12', '>= 1.2.12'
41
+ s.add_runtime_dependency 'simple_command_line_parser', '~> 1.1.2', '>= 1.1.2'
42
+ s.add_runtime_dependency 'simple_cloud_logging', '~> 1.2.2', '>= 1.2.2'
43
+ end