serial_scheduler 0.1.0 → 0.2.0

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 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