serial_scheduler 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b855f319188736942eaaf5c84dc08d5086a4f60d8514e8399551113786ddad79
4
- data.tar.gz: bfc620ad6f8420cae7d075e90a21f621973df3a255d5790d95d63fc464936bb0
3
+ metadata.gz: a3176f9491c9ca41665e74892f0ec5cca612b2590ef5d259eb595f03e3a80c1a
4
+ data.tar.gz: 182974bb8a089dc7c229eaa9a23294f88cea9d17b5eaca68f8a98cc9b0fe6bc6
5
5
  SHA512:
6
- metadata.gz: 5456139c9c0be079b948f019524b8a174741a36eaf1262e88aeeb0c4007962f60fff671aaafc663cf53485e4181b1eb801d57b97f98c33d08aa18c637e72df18
7
- data.tar.gz: 8efded31e1e11097706afdf0660bf489c11119d6cc81f1ab3c4f0c16a33ec9454080b848636df69fa253dd8e50dbdf13ca681c839f3a8c21bd0664c5295935be
6
+ metadata.gz: 4f33dd9831bea10233bac83fb3dfec909ef297f5f78254dea7e87babd6191210da35c81e572762292238258d0a95faf161d957c70d6cca688bedda3e5b8cb461
7
+ data.tar.gz: 5210390830383771b3fdcc69756116fd644f452e58b2c41ac6cfe3ef981329ac09c5792f3a6b0faefb8599652051dcfe4bbee4c5802ad5ab059a240509a80cfd
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  class SerialScheduler
3
- VERSION = "0.1.0"
3
+ VERSION = "0.2.0"
4
4
  end
@@ -5,6 +5,44 @@ require 'json'
5
5
  require 'logger'
6
6
 
7
7
  class SerialScheduler
8
+ class Producer
9
+ attr_reader :name, :next, :timeout, :block
10
+
11
+ def initialize(name, interval: nil, timeout:, cron: nil, &block)
12
+ if cron
13
+ cron = Fugit.do_parse_cron(cron)
14
+ elsif !interval || interval < 1 || !interval.is_a?(Integer)
15
+ raise ArgumentError
16
+ end
17
+
18
+ @name = name
19
+ @interval = interval
20
+ @cron = cron
21
+ @timeout = timeout
22
+ @block = block
23
+ @next = nil
24
+ end
25
+
26
+ def start(now)
27
+ @next =
28
+ if @cron
29
+ @cron.next_time(Time.at(now)).to_i
30
+ else
31
+ # interval 1s: do not wait
32
+ # interval 1d: if we are 1 hour into the day next execution is in 23 hours
33
+ now + (@interval - (now % @interval) - 1)
34
+ end
35
+ end
36
+
37
+ def next!
38
+ if @cron
39
+ @next = @cron.next_time(Time.at(@next)).to_i
40
+ else
41
+ @next += @interval
42
+ end
43
+ end
44
+ end
45
+
8
46
  def initialize(logger: Logger.new(STDOUT), error_handler: ->(e) { raise e })
9
47
  @logger = logger
10
48
  @error_handler = error_handler
@@ -14,24 +52,21 @@ class SerialScheduler
14
52
  end
15
53
 
16
54
  # start a new thread that enqueues an execution at given interval
17
- def add(name, interval:, timeout:, &block)
18
- raise ArgumentError if interval < 1 || !interval.is_a?(Integer)
19
-
20
- @producers << { name: name, interval: interval, timeout: timeout, block: block, next: 0 }
55
+ def add(*args, &block)
56
+ @producers << Producer.new(*args, &block)
21
57
  end
22
58
 
23
59
  def run
24
- # interval 1s: do not wait
25
- # interval 1d: if we are 1 hour into the day next execution is in 23 hours
26
60
  now = Time.now.to_i
27
- @producers.each { |p| p[:next] = now + (p[:interval] - (now % p[:interval]) - 1) }
61
+ @producers.each { |p| p.start now }
28
62
 
29
63
  loop do
30
- earliest = @producers.min_by { |p| p[:next] }
31
- wait = [earliest[:next] - Time.now.to_i, 0].max
64
+ now = Time.now.to_i
65
+ earliest = @producers.min_by(&:next)
66
+ wait = [earliest.next - now, 0].max # do not wait when overdue
32
67
 
33
68
  if wait > 0
34
- @logger.info message: "Waiting to start job", job: earliest[:name], time: wait
69
+ @logger.info message: "Waiting to start job", job: earliest.name, time: wait, at: Time.at(now).to_s
35
70
  wait.times do
36
71
  break if @stopped
37
72
 
@@ -40,7 +75,7 @@ class SerialScheduler
40
75
  end
41
76
  break if @stopped
42
77
 
43
- earliest[:next] += earliest[:interval]
78
+ earliest.next!
44
79
  execute_in_fork earliest
45
80
  end
46
81
  end
@@ -52,12 +87,12 @@ class SerialScheduler
52
87
  private
53
88
 
54
89
  def execute_in_fork(producer)
55
- @logger.info message: "Executing job", job: producer[:name]
90
+ @logger.info message: "Executing job", job: producer.name
56
91
  pid = fork do
57
92
  begin
58
- Timeout.timeout producer[:timeout], &producer[:block]
93
+ Timeout.timeout producer.timeout, &producer.block
59
94
  rescue StandardError => e # do not rescue `Exception` so it can be `Interrupt`-ed
60
- @logger.error message: "Error in job", job: producer[:name], error: e.message
95
+ @logger.error message: "Error in job", job: producer.name, error: e.message
61
96
  @error_handler.call(e)
62
97
  end
63
98
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: serial_scheduler
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Grosser
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-12-31 00:00:00.000000000 Z
12
- dependencies: []
11
+ date: 2020-03-14 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: fugit
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  description:
14
28
  email: michael@grosser.it
15
29
  executables: []
@@ -38,8 +52,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
38
52
  - !ruby/object:Gem::Version
39
53
  version: '0'
40
54
  requirements: []
41
- rubyforge_project:
42
- rubygems_version: 2.7.6
55
+ rubygems_version: 3.0.3
43
56
  signing_key:
44
57
  specification_version: 4
45
58
  summary: Simple scheduler for long-running and infrequent tasks, no threads, always