chutzen 0.8.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 +7 -0
- data/README.md +244 -0
- data/lib/chutzen/apply.rb +49 -0
- data/lib/chutzen/command/execution_failed.rb +42 -0
- data/lib/chutzen/command.rb +216 -0
- data/lib/chutzen/demux.rb +35 -0
- data/lib/chutzen/dictionary.rb +135 -0
- data/lib/chutzen/expression/lookup_error.rb +30 -0
- data/lib/chutzen/expression/syntax_error.rb +28 -0
- data/lib/chutzen/expression.rb +74 -0
- data/lib/chutzen/expression_parser.rb +47 -0
- data/lib/chutzen/job.rb +80 -0
- data/lib/chutzen/notification.rb +32 -0
- data/lib/chutzen/runtime_error.rb +21 -0
- data/lib/chutzen/shell.rb +27 -0
- data/lib/chutzen/signal.rb +49 -0
- data/lib/chutzen/standard_error.rb +10 -0
- data/lib/chutzen/template/syntax_error.rb +28 -0
- data/lib/chutzen/template.rb +39 -0
- data/lib/chutzen/template_parser.rb +17 -0
- data/lib/chutzen/version.rb +5 -0
- data/lib/chutzen/watcher.rb +111 -0
- data/lib/chutzen/worker.rb +16 -0
- data/lib/chutzen.rb +87 -0
- metadata +136 -0
@@ -0,0 +1,111 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Chutzen
|
4
|
+
# Watches the current process to make sure it's still running as intended.
|
5
|
+
#
|
6
|
+
# You can set a watcher to make sure a list of files increases in size at
|
7
|
+
# least every ‘read_timeout’ seconds.
|
8
|
+
#
|
9
|
+
# watcher = Watcher.new(
|
10
|
+
# fail_when: {
|
11
|
+
# 'read_timeout' => 2,
|
12
|
+
# 'runtime' => 60
|
13
|
+
# },
|
14
|
+
# files: [file]
|
15
|
+
# )
|
16
|
+
# until watcher.done?
|
17
|
+
# watcher.tick
|
18
|
+
# end
|
19
|
+
class Watcher
|
20
|
+
class Error < StandardError
|
21
|
+
end
|
22
|
+
|
23
|
+
attr_reader :read_timeout, :runtime
|
24
|
+
|
25
|
+
def initialize(fail_when: nil, files: nil)
|
26
|
+
fail_when&.each do |name, value|
|
27
|
+
instance_variable_set("@#{name}", value)
|
28
|
+
end
|
29
|
+
@started_at = now
|
30
|
+
@files = files
|
31
|
+
reset
|
32
|
+
end
|
33
|
+
|
34
|
+
def select_timeout
|
35
|
+
return @select_timeout if defined?(@select_timeout)
|
36
|
+
|
37
|
+
minimal_timeout = [read_timeout, runtime].compact.min
|
38
|
+
return unless minimal_timeout
|
39
|
+
|
40
|
+
minimal_timeout / 2.0
|
41
|
+
end
|
42
|
+
|
43
|
+
def done?
|
44
|
+
# Because demuxers and the watcher share an interface we need to the
|
45
|
+
# watcher to explain the thread doesn't have to wait for it.
|
46
|
+
true
|
47
|
+
end
|
48
|
+
|
49
|
+
# Raises an exception when any of the failure conditions is reached.
|
50
|
+
def tick
|
51
|
+
compute_last_read
|
52
|
+
verify_last_read
|
53
|
+
verify_runtime
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def total_bytes_read
|
59
|
+
return unless @files
|
60
|
+
|
61
|
+
@files.inject(0) { |total, file| total + file.size }
|
62
|
+
end
|
63
|
+
|
64
|
+
def last_read_ago
|
65
|
+
now - @last_read_at
|
66
|
+
end
|
67
|
+
|
68
|
+
def compute_last_read
|
69
|
+
return unless read_timeout && @files
|
70
|
+
|
71
|
+
bytes_read = total_bytes_read
|
72
|
+
@last_read_at = now if bytes_read != @bytes_read
|
73
|
+
@bytes_read = bytes_read
|
74
|
+
end
|
75
|
+
|
76
|
+
def verify_last_read
|
77
|
+
return unless read_timeout && @files
|
78
|
+
return if last_read_ago < read_timeout
|
79
|
+
|
80
|
+
raise(
|
81
|
+
Error,
|
82
|
+
"Command did not write to its output for more than #{read_timeout} " \
|
83
|
+
'seconds.'
|
84
|
+
)
|
85
|
+
end
|
86
|
+
|
87
|
+
def ran
|
88
|
+
now - @started_at
|
89
|
+
end
|
90
|
+
|
91
|
+
def verify_runtime
|
92
|
+
return unless runtime
|
93
|
+
return if ran < runtime
|
94
|
+
|
95
|
+
raise(
|
96
|
+
Error,
|
97
|
+
"Command did not finish within the alotted runtime #{runtime} " \
|
98
|
+
'seconds.'
|
99
|
+
)
|
100
|
+
end
|
101
|
+
|
102
|
+
def reset
|
103
|
+
@last_read_at = now
|
104
|
+
@bytes_read = 0
|
105
|
+
end
|
106
|
+
|
107
|
+
def now
|
108
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'sidekiq'
|
4
|
+
|
5
|
+
module Chutzen
|
6
|
+
# Sidekiq worker to perform a Chutzen job expressed in JSON.
|
7
|
+
class Worker
|
8
|
+
include Sidekiq::Worker
|
9
|
+
|
10
|
+
sidekiq_options queue: Chutzen.sidekiq_queue
|
11
|
+
|
12
|
+
def perform(json)
|
13
|
+
Chutzen.perform(JSON.parse(json))
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/chutzen.rb
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'sidekiq/api'
|
5
|
+
|
6
|
+
# Disabled to allow optional loading of the New Relic gem.
|
7
|
+
# rubocop:disable Lint/SuppressedException
|
8
|
+
begin
|
9
|
+
require 'newrelic_rpm'
|
10
|
+
rescue LoadError
|
11
|
+
end
|
12
|
+
# rubocop:enable Lint/SuppressedException
|
13
|
+
|
14
|
+
# Implements a toolkit to perform batch processing.
|
15
|
+
module Chutzen
|
16
|
+
autoload :Apply, 'chutzen/apply'
|
17
|
+
autoload :Command, 'chutzen/command'
|
18
|
+
autoload :Demux, 'chutzen/demux'
|
19
|
+
autoload :Dictionary, 'chutzen/dictionary'
|
20
|
+
autoload :Expression, 'chutzen/expression'
|
21
|
+
autoload :ExpressionParser, 'chutzen/expression_parser'
|
22
|
+
autoload :Job, 'chutzen/job'
|
23
|
+
autoload :Notification, 'chutzen/notification'
|
24
|
+
autoload :RuntimeError, 'chutzen/runtime_error'
|
25
|
+
autoload :Shell, 'chutzen/shell'
|
26
|
+
autoload :Signal, 'chutzen/signal'
|
27
|
+
autoload :StandardError, 'chutzen/standard_error'
|
28
|
+
autoload :Template, 'chutzen/template'
|
29
|
+
autoload :TemplateParser, 'chutzen/template_parser'
|
30
|
+
autoload :VERSION, 'chutzen/version'
|
31
|
+
autoload :Watcher, 'chutzen/watcher'
|
32
|
+
autoload :Worker, 'chutzen/worker'
|
33
|
+
|
34
|
+
class << self
|
35
|
+
attr_accessor :sidekiq_queue
|
36
|
+
end
|
37
|
+
self.sidekiq_queue = 'chutzen'
|
38
|
+
|
39
|
+
# Enqueue a Sidekiq job that will eventually perform the job in the
|
40
|
+
# description.
|
41
|
+
def self.perform_async(description)
|
42
|
+
Chutzen::Worker.perform_async(JSON.dump(description))
|
43
|
+
end
|
44
|
+
|
45
|
+
# Enqueue a Sidekiq job that will eventually process the signal. It's
|
46
|
+
# customary for each Chutzen host to have its own control queue next to
|
47
|
+
# the regular job queue. Use the name of that queue when you want to
|
48
|
+
# target a specific host. Otherwise the signal will be processed by one
|
49
|
+
# random host.
|
50
|
+
#
|
51
|
+
# @example Stop chutzen-1-staging
|
52
|
+
# Chutzen.emit_signal('stop', queue: 'chutzen-1-staging')
|
53
|
+
def self.emit_signal(signal, queue:, expires_at: nil, expires_in: nil)
|
54
|
+
Chutzen::Signal.set(
|
55
|
+
queue: queue
|
56
|
+
).perform_async(
|
57
|
+
signal,
|
58
|
+
expiration_since_epoch(
|
59
|
+
expires_at: expires_at,
|
60
|
+
expires_in: expires_in
|
61
|
+
)
|
62
|
+
)
|
63
|
+
end
|
64
|
+
|
65
|
+
# Dequeue all jobs that match the query. Uses Chutzen's own query format.
|
66
|
+
def self.dequeue(query)
|
67
|
+
Sidekiq::Queue.new(sidekiq_queue).each do |job|
|
68
|
+
dictionary = Chutzen::Dictionary.new(JSON.parse(job.args.first))
|
69
|
+
expression = Chutzen::Expression.new(query, dictionary)
|
70
|
+
job.delete if expression.result
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# Immediately perform the description.
|
75
|
+
def self.perform(description)
|
76
|
+
Chutzen::Job.new(description).perform
|
77
|
+
end
|
78
|
+
|
79
|
+
# Computes the expiration in seconds since epoch. When neither expires_at
|
80
|
+
# or expires_in is specified it will default to 30 seconds from now.
|
81
|
+
def self.expiration_since_epoch(expires_at: nil, expires_in: nil)
|
82
|
+
return expires_at.to_i if expires_at
|
83
|
+
|
84
|
+
expires_in ||= 30
|
85
|
+
(Time.now + expires_in).to_i
|
86
|
+
end
|
87
|
+
end
|
metadata
ADDED
@@ -0,0 +1,136 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: chutzen
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.8.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Manfred Stienstra
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2023-04-02 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: parslet
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: sidekiq
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '6.0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '6.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: newrelic_rpm
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '6.10'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '6.10'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: minitest
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '5.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '5.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rake
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '13.0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '13.0'
|
83
|
+
description: |
|
84
|
+
Chutzen is a toolkit to implement batch processing for your Ruby or
|
85
|
+
Ruby on Rails application.
|
86
|
+
email:
|
87
|
+
executables: []
|
88
|
+
extensions: []
|
89
|
+
extra_rdoc_files: []
|
90
|
+
files:
|
91
|
+
- README.md
|
92
|
+
- lib/chutzen.rb
|
93
|
+
- lib/chutzen/apply.rb
|
94
|
+
- lib/chutzen/command.rb
|
95
|
+
- lib/chutzen/command/execution_failed.rb
|
96
|
+
- lib/chutzen/demux.rb
|
97
|
+
- lib/chutzen/dictionary.rb
|
98
|
+
- lib/chutzen/expression.rb
|
99
|
+
- lib/chutzen/expression/lookup_error.rb
|
100
|
+
- lib/chutzen/expression/syntax_error.rb
|
101
|
+
- lib/chutzen/expression_parser.rb
|
102
|
+
- lib/chutzen/job.rb
|
103
|
+
- lib/chutzen/notification.rb
|
104
|
+
- lib/chutzen/runtime_error.rb
|
105
|
+
- lib/chutzen/shell.rb
|
106
|
+
- lib/chutzen/signal.rb
|
107
|
+
- lib/chutzen/standard_error.rb
|
108
|
+
- lib/chutzen/template.rb
|
109
|
+
- lib/chutzen/template/syntax_error.rb
|
110
|
+
- lib/chutzen/template_parser.rb
|
111
|
+
- lib/chutzen/version.rb
|
112
|
+
- lib/chutzen/watcher.rb
|
113
|
+
- lib/chutzen/worker.rb
|
114
|
+
homepage:
|
115
|
+
licenses: []
|
116
|
+
metadata: {}
|
117
|
+
post_install_message:
|
118
|
+
rdoc_options: []
|
119
|
+
require_paths:
|
120
|
+
- lib
|
121
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
122
|
+
requirements:
|
123
|
+
- - ">="
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '2.7'
|
126
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
127
|
+
requirements:
|
128
|
+
- - ">="
|
129
|
+
- !ruby/object:Gem::Version
|
130
|
+
version: '0'
|
131
|
+
requirements: []
|
132
|
+
rubygems_version: 3.3.7
|
133
|
+
signing_key:
|
134
|
+
specification_version: 4
|
135
|
+
summary: Toolkit to implement batch processing.
|
136
|
+
test_files: []
|