serial_scheduler 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: b855f319188736942eaaf5c84dc08d5086a4f60d8514e8399551113786ddad79
4
+ data.tar.gz: bfc620ad6f8420cae7d075e90a21f621973df3a255d5790d95d63fc464936bb0
5
+ SHA512:
6
+ metadata.gz: 5456139c9c0be079b948f019524b8a174741a36eaf1262e88aeeb0c4007962f60fff671aaafc663cf53485e4181b1eb801d57b97f98c33d08aa18c637e72df18
7
+ data.tar.gz: 8efded31e1e11097706afdf0660bf489c11119d6cc81f1ab3c4f0c16a33ec9454080b848636df69fa253dd8e50dbdf13ca681c839f3a8c21bd0664c5295935be
@@ -0,0 +1,20 @@
1
+ Copyright (C) 2013 Michael Grosser <michael@grosser.it>
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'timeout'
4
+ require 'json'
5
+ require 'logger'
6
+
7
+ class SerialScheduler
8
+ def initialize(logger: Logger.new(STDOUT), error_handler: ->(e) { raise e })
9
+ @logger = logger
10
+ @error_handler = error_handler
11
+
12
+ @producers = []
13
+ @stopped = false
14
+ end
15
+
16
+ # 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 }
21
+ end
22
+
23
+ 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
+ now = Time.now.to_i
27
+ @producers.each { |p| p[:next] = now + (p[:interval] - (now % p[:interval]) - 1) }
28
+
29
+ loop do
30
+ earliest = @producers.min_by { |p| p[:next] }
31
+ wait = [earliest[:next] - Time.now.to_i, 0].max
32
+
33
+ if wait > 0
34
+ @logger.info message: "Waiting to start job", job: earliest[:name], time: wait
35
+ wait.times do
36
+ break if @stopped
37
+
38
+ sleep 1
39
+ end
40
+ end
41
+ break if @stopped
42
+
43
+ earliest[:next] += earliest[:interval]
44
+ execute_in_fork earliest
45
+ end
46
+ end
47
+
48
+ def stop
49
+ @stopped = true
50
+ end
51
+
52
+ private
53
+
54
+ def execute_in_fork(producer)
55
+ @logger.info message: "Executing job", job: producer[:name]
56
+ pid = fork do
57
+ begin
58
+ Timeout.timeout producer[:timeout], &producer[:block]
59
+ 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
61
+ @error_handler.call(e)
62
+ end
63
+ end
64
+ Process.wait pid
65
+ end
66
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+ class SerialScheduler
3
+ VERSION = "0.1.0"
4
+ end
metadata ADDED
@@ -0,0 +1,47 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: serial_scheduler
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Michael Grosser
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-12-31 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description:
14
+ email: michael@grosser.it
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - MIT-LICENSE
20
+ - lib/serial_scheduler.rb
21
+ - lib/serial_scheduler/version.rb
22
+ homepage: https://github.com/grosser/serial_scheduler
23
+ licenses:
24
+ - MIT
25
+ metadata: {}
26
+ post_install_message:
27
+ rdoc_options: []
28
+ require_paths:
29
+ - lib
30
+ required_ruby_version: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: 2.3.0
35
+ required_rubygems_version: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ requirements: []
41
+ rubyforge_project:
42
+ rubygems_version: 2.7.6
43
+ signing_key:
44
+ specification_version: 4
45
+ summary: Simple scheduler for long-running and infrequent tasks, no threads, always
46
+ in serial
47
+ test_files: []