rflow 1.0.0a1 → 1.0.0a2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/.rspec +1 -0
- data/Gemfile +0 -1
- data/NOTES +0 -13
- data/README.md +6 -1
- data/bin/rflow +2 -9
- data/example/basic_config.rb +1 -33
- data/example/basic_extensions.rb +0 -98
- data/example/http_config.rb +2 -3
- data/example/http_extensions.rb +6 -63
- data/lib/rflow.rb +31 -39
- data/lib/rflow/child_process.rb +112 -0
- data/lib/rflow/component.rb +77 -148
- data/lib/rflow/component/port.rb +38 -41
- data/lib/rflow/components.rb +4 -8
- data/lib/rflow/components/clock.rb +49 -0
- data/lib/rflow/components/integer.rb +39 -0
- data/lib/rflow/components/raw.rb +10 -6
- data/lib/rflow/components/replicate.rb +20 -0
- data/lib/rflow/components/ruby_proc_filter.rb +27 -0
- data/lib/rflow/configuration.rb +105 -184
- data/lib/rflow/configuration/component.rb +1 -4
- data/lib/rflow/configuration/connection.rb +11 -16
- data/lib/rflow/configuration/port.rb +3 -5
- data/lib/rflow/configuration/ruby_dsl.rb +105 -119
- data/lib/rflow/configuration/setting.rb +19 -25
- data/lib/rflow/configuration/shard.rb +1 -3
- data/lib/rflow/connection.rb +47 -10
- data/lib/rflow/connections.rb +0 -1
- data/lib/rflow/connections/zmq_connection.rb +34 -38
- data/lib/rflow/daemon_process.rb +155 -0
- data/lib/rflow/logger.rb +41 -25
- data/lib/rflow/master.rb +23 -105
- data/lib/rflow/message.rb +78 -108
- data/lib/rflow/pid_file.rb +37 -37
- data/lib/rflow/shard.rb +33 -100
- data/lib/rflow/version.rb +2 -2
- data/rflow.gemspec +2 -2
- data/schema/tick.avsc +10 -0
- data/spec/fixtures/config_ints.rb +4 -40
- data/spec/fixtures/config_shards.rb +1 -2
- data/spec/fixtures/extensions_ints.rb +0 -98
- data/spec/rflow/component/port_spec.rb +61 -0
- data/spec/rflow/components/clock_spec.rb +72 -0
- data/spec/rflow/configuration/ruby_dsl_spec.rb +150 -0
- data/spec/rflow/configuration_spec.rb +54 -0
- data/spec/rflow/forward_to_input_port_spec.rb +48 -0
- data/spec/rflow/forward_to_output_port_spec.rb +40 -0
- data/spec/rflow/logger_spec.rb +48 -0
- data/spec/rflow/message/data/raw_spec.rb +29 -0
- data/spec/rflow/message/data_spec.rb +58 -0
- data/spec/rflow/message_spec.rb +154 -0
- data/spec/rflow_spec.rb +94 -124
- data/spec/spec_helper.rb +8 -12
- metadata +46 -22
- data/lib/rflow/components/raw/extensions.rb +0 -18
- data/lib/rflow/port.rb +0 -4
- data/lib/rflow/util.rb +0 -19
- data/spec/rflow_component_port_spec.rb +0 -58
- data/spec/rflow_configuration_ruby_dsl_spec.rb +0 -148
- data/spec/rflow_configuration_spec.rb +0 -73
- data/spec/rflow_message_data_raw.rb +0 -26
- data/spec/rflow_message_data_spec.rb +0 -60
- data/spec/rflow_message_spec.rb +0 -182
- data/spec/schema_spec.rb +0 -28
- data/temp.rb +0 -295
data/lib/rflow/master.rb
CHANGED
@@ -1,127 +1,45 @@
|
|
1
|
+
require 'rflow/daemon_process'
|
1
2
|
require 'rflow/pid_file'
|
2
3
|
require 'rflow/shard'
|
3
4
|
|
4
5
|
class RFlow
|
5
|
-
class Master
|
6
|
-
|
7
|
-
attr_accessor :name, :pid_file, :ready_write
|
8
|
-
attr_accessor :shards
|
6
|
+
class Master < DaemonProcess
|
7
|
+
attr_reader :shards
|
9
8
|
|
10
9
|
def initialize(config)
|
11
|
-
|
10
|
+
super(config['rflow.application_name'], 'Master')
|
12
11
|
@pid_file = PIDFile.new(config['rflow.pid_file_path'])
|
13
|
-
@shards = config.shards.map
|
14
|
-
Shard.new(shard_config)
|
15
|
-
end
|
12
|
+
@shards = config.shards.map {|config| Shard.new(config) }
|
16
13
|
end
|
17
14
|
|
18
|
-
def
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
Thread.new { shutdown(signal) }.join
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
# Reopen logs on USR1
|
28
|
-
['SIGUSR1'].each do |signal|
|
29
|
-
Signal.trap signal do
|
30
|
-
Thread.new do
|
31
|
-
RFlow.logger.reopen
|
32
|
-
signal_workers(signal)
|
33
|
-
end.join
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
# Toggle log level on USR2
|
38
|
-
['SIGUSR2'].each do |signal|
|
39
|
-
Signal.trap signal do
|
40
|
-
Thread.new do
|
41
|
-
RFlow.logger.toggle_log_level
|
42
|
-
signal_workers(signal)
|
43
|
-
end.join
|
44
|
-
end
|
45
|
-
end
|
15
|
+
def run!
|
16
|
+
write_pid_file
|
17
|
+
super
|
18
|
+
ensure
|
19
|
+
remove_pid_file
|
46
20
|
end
|
47
21
|
|
48
|
-
def
|
49
|
-
|
50
|
-
Log4r::NDC.push name
|
51
|
-
$0 = name
|
52
|
-
|
53
|
-
shards.each {|s| s.run!}
|
54
|
-
|
55
|
-
handle_signals
|
56
|
-
|
57
|
-
# Signal the grandparent that we are running
|
58
|
-
if ready_write
|
59
|
-
ready_write.syswrite($$.to_s)
|
60
|
-
ready_write.close rescue nil
|
61
|
-
end
|
62
|
-
|
63
|
-
pid_file.write
|
64
|
-
|
65
|
-
RFlow.logger.info "Master started"
|
66
|
-
|
67
|
-
EM.run do
|
68
|
-
# TODO: Monitor the workers
|
69
|
-
end
|
70
|
-
|
71
|
-
@pid_file.safe_unlink
|
22
|
+
def spawn_subprocesses
|
23
|
+
shards.each(&:run!)
|
72
24
|
end
|
73
25
|
|
74
|
-
def
|
75
|
-
|
76
|
-
|
77
|
-
ready_read, @ready_write = IO.pipe
|
78
|
-
[ready_read, @ready_write].each { |io| io.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) }
|
79
|
-
|
80
|
-
grandparent = $$
|
81
|
-
|
82
|
-
if fork
|
83
|
-
# Grandparent waits for a PID on the pipe indicating that the
|
84
|
-
# master successfully started.
|
85
|
-
@ready_write.close # grandparent does not write
|
86
|
-
master_pid = (ready_read.readpartial(16) rescue nil).to_i
|
87
|
-
unless master_pid > 1
|
88
|
-
RFlow.logger.error "Master failed to start"
|
89
|
-
exit! 1
|
90
|
-
end
|
91
|
-
RFlow.logger.info "Master indicated successful daemonization"
|
92
|
-
exit 0
|
93
|
-
end
|
94
|
-
|
95
|
-
Process.daemon(true, true)
|
96
|
-
|
97
|
-
ready_read.close # master does not read
|
98
|
-
|
99
|
-
# Close standard IO
|
100
|
-
$stdout.sync = $stderr.sync = true
|
101
|
-
$stdin.binmode; $stdout.binmode; $stderr.binmode
|
102
|
-
begin; $stdin.reopen "/dev/null"; rescue ::Exception; end
|
103
|
-
begin; $stdout.reopen "/dev/null"; rescue ::Exception; end
|
104
|
-
begin; $stderr.reopen "/dev/null"; rescue ::Exception; end
|
105
|
-
|
106
|
-
$$
|
26
|
+
def subprocesses
|
27
|
+
shards.flat_map(&:workers)
|
107
28
|
end
|
108
29
|
|
109
|
-
def
|
110
|
-
|
111
|
-
|
112
|
-
RFlow.logger.info "Signalling #{worker.name} with #{signal}"
|
113
|
-
Process.kill(signal, worker.pid)
|
114
|
-
end
|
30
|
+
def run_process
|
31
|
+
EM.run do
|
32
|
+
# TODO: Monitor the workers
|
115
33
|
end
|
116
34
|
end
|
117
35
|
|
118
|
-
def shutdown(reason)
|
119
|
-
|
120
|
-
|
121
|
-
pid_file.safe_unlink
|
122
|
-
RFlow.logger.info "#{name} exiting"
|
123
|
-
exit 0
|
36
|
+
def shutdown!(reason)
|
37
|
+
remove_pid_file
|
38
|
+
super
|
124
39
|
end
|
125
40
|
|
41
|
+
private
|
42
|
+
def write_pid_file; @pid_file.write; end
|
43
|
+
def remove_pid_file; @pid_file.safe_unlink; end
|
126
44
|
end
|
127
45
|
end
|
data/lib/rflow/message.rb
CHANGED
@@ -1,127 +1,108 @@
|
|
1
1
|
require 'stringio'
|
2
2
|
require 'time'
|
3
|
-
|
4
3
|
require 'avro'
|
5
|
-
|
6
4
|
require 'rflow/configuration'
|
7
5
|
|
8
6
|
class RFlow
|
7
|
+
class Avro
|
8
|
+
def self.decode(reader, bytes)
|
9
|
+
reader.read ::Avro::IO::BinaryDecoder.new(StringIO.new(bytes.force_encoding('BINARY')))
|
10
|
+
end
|
9
11
|
|
10
|
-
|
11
|
-
|
12
|
-
|
12
|
+
def self.encode(writer, message)
|
13
|
+
String.new.force_encoding('BINARY').tap do |result|
|
14
|
+
writer.write message, ::Avro::IO::BinaryEncoder.new(StringIO.new(result, 'w'))
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
13
18
|
|
19
|
+
class Message
|
14
20
|
class << self
|
15
|
-
def
|
16
|
-
|
17
|
-
def
|
18
|
-
def
|
19
|
-
def avro_decoder(io_object); Avro::IO::BinaryDecoder.new(io_object); end
|
20
|
-
def avro_encoder(io_object); Avro::IO::BinaryEncoder.new(io_object); end
|
21
|
+
def schema; @schema ||= ::Avro::Schema.parse(File.read(File.join(File.dirname(__FILE__), '..', '..', 'schema', 'message.avsc'))); end
|
22
|
+
def message_reader; @message_reader ||= ::Avro::IO::DatumReader.new(schema, schema); end
|
23
|
+
def message_writer; @message_writer ||= ::Avro::IO::DatumWriter.new(schema); end
|
24
|
+
def encode(message); RFlow::Avro.encode(message_writer, message); end
|
21
25
|
|
22
26
|
# Take in an Avro serialization of a message and return a new
|
23
27
|
# Message object. Assumes the org.rflow.Message Avro schema.
|
24
|
-
def from_avro(
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
message_hash['data'])
|
28
|
+
def from_avro(bytes)
|
29
|
+
message = RFlow::Avro.decode(message_reader, bytes)
|
30
|
+
Message.new(message['data_type_name'], message['provenance'],
|
31
|
+
message['data_serialization_type'], message['data_schema'],
|
32
|
+
message['data'])
|
30
33
|
end
|
31
34
|
end
|
32
35
|
|
33
|
-
|
34
|
-
# Serialize the current message object to Avro using the
|
35
|
-
# org.rflow.Message Avro schema. Note that we have to manually
|
36
|
-
# set the encoding for Ruby 1.9, otherwise the stringio would use
|
37
|
-
# UTF-8 by default, which would not work correctly, as a serialize
|
38
|
-
# avro string is BINARY, not UTF-8
|
39
|
-
def to_avro
|
40
|
-
avro_serialized_message_bytes = ''
|
41
|
-
avro_serialized_message_bytes.force_encoding 'BINARY'
|
42
|
-
avro_serialized_message_bytes_stringio = StringIO.new(avro_serialized_message_bytes, 'w')
|
43
|
-
|
44
|
-
deserialized_avro_object = {
|
45
|
-
'data_type_name' => self.data_type_name.to_s,
|
46
|
-
'provenance' => self.provenance.map(&:to_hash),
|
47
|
-
'data_serialization_type' => self.data.serialization_type.to_s,
|
48
|
-
'data_schema' => self.data.schema_string,
|
49
|
-
'data' => self.data.to_avro
|
50
|
-
}
|
51
|
-
|
52
|
-
self.class.avro_writer.write deserialized_avro_object, self.class.avro_encoder(avro_serialized_message_bytes_stringio)
|
53
|
-
avro_serialized_message_bytes
|
54
|
-
end
|
55
|
-
|
56
|
-
|
57
|
-
attr_reader :data_type_name
|
58
|
-
attr_accessor :processing_event
|
36
|
+
attr_reader :data_type_name, :data
|
59
37
|
attr_accessor :provenance
|
60
|
-
attr_reader :data, :data_extensions
|
61
38
|
|
62
|
-
|
63
|
-
def initialize(data_type_name, provenance=[], data_serialization_type='avro', data_schema_string=nil, serialized_data_object=nil)
|
64
|
-
# Default the values, in case someone puts in a nil instead of
|
65
|
-
# the default
|
39
|
+
def initialize(data_type_name, provenance = [], serialization_type = 'avro', schema = nil, serialized_data = nil)
|
66
40
|
@data_type_name = data_type_name.to_s
|
67
41
|
|
68
|
-
# Turn the provenance array of Hashes into an array of
|
69
|
-
# ProcessingEvents for easier access and time validation. TODO:
|
70
|
-
# do this lazily so as not to create/destroy objects that are
|
71
|
-
# never used
|
72
|
-
@provenance = (provenance || []).map do |processing_event_hash_or_object|
|
73
|
-
if processing_event_hash_or_object.is_a? ProcessingEvent
|
74
|
-
processing_event_hash_or_object
|
75
|
-
else
|
76
|
-
ProcessingEvent.new(processing_event_hash_or_object['component_instance_uuid'],
|
77
|
-
processing_event_hash_or_object['started_at'],
|
78
|
-
processing_event_hash_or_object['completed_at'],
|
79
|
-
processing_event_hash_or_object['context'])
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
42
|
# TODO: Make this better. This check is technically
|
84
|
-
# unnecessary, as we are able to completely
|
43
|
+
# unnecessary, as we are able to completely deserialize the
|
85
44
|
# message without needing to resort to the registered schema.
|
86
|
-
|
87
|
-
unless
|
88
|
-
|
89
|
-
RFlow.logger.error error_message
|
90
|
-
raise ArgumentError, error_message
|
45
|
+
registered_schema = RFlow::Configuration.available_data_types[@data_type_name][serialization_type.to_s]
|
46
|
+
unless registered_schema
|
47
|
+
raise ArgumentError, "Data type '#{@data_type_name}' with serialization_type '#{serialization_type}' not found"
|
91
48
|
end
|
92
49
|
|
93
50
|
# TODO: think about registering the schemas automatically if not
|
94
51
|
# found in Configuration
|
95
|
-
if
|
96
|
-
|
97
|
-
|
98
|
-
|
52
|
+
if schema && (registered_schema != schema)
|
53
|
+
raise ArgumentError, "Passed schema ('#{schema}') does not equal registered schema ('#{registered_schema}') for data type '#{@data_type_name}' with serialization_type '#{serialization_type}'"
|
54
|
+
end
|
55
|
+
|
56
|
+
# Turn the provenance array of Hashes into an array of
|
57
|
+
# ProcessingEvents for easier access and time validation.
|
58
|
+
# TODO: do this lazily so as not to create/destroy objects that are
|
59
|
+
# never used
|
60
|
+
@provenance = (provenance || []).map do |event|
|
61
|
+
if event.is_a? ProcessingEvent
|
62
|
+
event
|
63
|
+
else
|
64
|
+
ProcessingEvent.new(event['component_instance_uuid'],
|
65
|
+
event['started_at'], event['completed_at'],
|
66
|
+
event['context'])
|
67
|
+
end
|
99
68
|
end
|
100
69
|
|
101
|
-
@data = Data.new(
|
70
|
+
@data = Data.new(registered_schema, serialization_type.to_s, serialized_data)
|
102
71
|
|
103
72
|
# Get the extensions and apply them to the data object to add capability
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
@data.extend data_extension
|
73
|
+
RFlow::Configuration.available_data_extensions[@data_type_name].each do |e|
|
74
|
+
RFlow.logger.debug "Extending '#{data_type_name}' with extension '#{e}'"
|
75
|
+
@data.extend e
|
108
76
|
end
|
109
77
|
end
|
110
78
|
|
79
|
+
# Serialize the current message object to Avro using the
|
80
|
+
# org.rflow.Message Avro schema. Note that we have to manually
|
81
|
+
# set the encoding for Ruby 1.9, otherwise the stringio would use
|
82
|
+
# UTF-8 by default, which would not work correctly, as a serialize
|
83
|
+
# avro string is BINARY, not UTF-8
|
84
|
+
def to_avro
|
85
|
+
Message.encode('data_type_name' => data_type_name.to_s,
|
86
|
+
'provenance' => provenance.map(&:to_hash),
|
87
|
+
'data_serialization_type' => data.serialization_type.to_s,
|
88
|
+
'data_schema' => data.schema_string,
|
89
|
+
'data' => data.to_avro)
|
90
|
+
end
|
111
91
|
|
112
92
|
class ProcessingEvent
|
113
|
-
|
93
|
+
attr_reader :component_instance_uuid, :started_at
|
94
|
+
attr_accessor :completed_at, :context
|
114
95
|
|
115
|
-
def initialize(component_instance_uuid, started_at=nil, completed_at=nil, context=nil)
|
96
|
+
def initialize(component_instance_uuid, started_at = nil, completed_at = nil, context = nil)
|
116
97
|
@component_instance_uuid = component_instance_uuid
|
117
98
|
@started_at = case started_at
|
118
|
-
when String
|
119
|
-
when Time
|
120
|
-
else
|
99
|
+
when String; Time.xmlschema(started_at)
|
100
|
+
when Time; started_at
|
101
|
+
else nil; end
|
121
102
|
@completed_at = case completed_at
|
122
|
-
when String
|
123
|
-
when Time
|
124
|
-
else
|
103
|
+
when String; Time.xmlschema(completed_at)
|
104
|
+
when Time; completed_at
|
105
|
+
else nil; end
|
125
106
|
@context = context
|
126
107
|
end
|
127
108
|
|
@@ -142,41 +123,31 @@ class RFlow
|
|
142
123
|
attr_reader :schema_string, :schema, :serialization_type
|
143
124
|
attr_accessor :data_object
|
144
125
|
|
145
|
-
def initialize(schema_string, serialization_type='avro',
|
146
|
-
unless serialization_type == 'avro'
|
147
|
-
error_message = "Only Avro serialization_type supported at the moment"
|
148
|
-
RFlow.logger.error error_message
|
149
|
-
raise ArgumentError, error_message
|
150
|
-
end
|
126
|
+
def initialize(schema_string, serialization_type = 'avro', serialized_data = nil)
|
127
|
+
raise ArgumentError, "Only Avro serialization_type supported at the moment" unless serialization_type.to_s == 'avro'
|
151
128
|
|
152
129
|
@schema_string = schema_string
|
153
|
-
@serialization_type = serialization_type
|
130
|
+
@serialization_type = serialization_type.to_s
|
154
131
|
|
155
132
|
begin
|
156
|
-
@schema = Avro::Schema.parse(schema_string)
|
133
|
+
@schema = ::Avro::Schema.parse(schema_string)
|
134
|
+
@writer = ::Avro::IO::DatumWriter.new(@schema)
|
157
135
|
rescue Exception => e
|
158
|
-
|
159
|
-
RFlow.logger.error error_message
|
160
|
-
raise ArgumentError, error_message
|
136
|
+
raise ArgumentError, "Invalid schema '#{@schema_string}': #{e}: #{e.message}"
|
161
137
|
end
|
162
138
|
|
163
|
-
if
|
164
|
-
|
165
|
-
|
166
|
-
@data_object = Avro::IO::DatumReader.new(schema, schema).read avro_decoder
|
139
|
+
if serialized_data
|
140
|
+
serialized_data.force_encoding 'BINARY'
|
141
|
+
@data_object = RFlow::Avro.decode(::Avro::IO::DatumReader.new(schema, schema), serialized_data)
|
167
142
|
end
|
168
143
|
end
|
169
144
|
|
170
145
|
def valid?
|
171
|
-
Avro::Schema.validate @schema, @data_object
|
146
|
+
::Avro::Schema.validate @schema, @data_object
|
172
147
|
end
|
173
148
|
|
174
149
|
def to_avro
|
175
|
-
|
176
|
-
serialized_data_object_bytes.force_encoding 'BINARY'
|
177
|
-
serialized_data_object_bytes_stringio = StringIO.new(serialized_data_object_bytes)
|
178
|
-
Avro::IO::DatumWriter.new(@schema).write @data_object, Avro::IO::BinaryEncoder.new(serialized_data_object_bytes_stringio)
|
179
|
-
serialized_data_object_bytes
|
150
|
+
RFlow::Avro.encode @writer, @data_object
|
180
151
|
end
|
181
152
|
|
182
153
|
# Proxy methods down to the underlying data_object, probably a
|
@@ -186,6 +157,5 @@ class RFlow
|
|
186
157
|
@data_object.send(method_sym, *args, &block)
|
187
158
|
end
|
188
159
|
end
|
189
|
-
|
190
160
|
end
|
191
161
|
end
|
data/lib/rflow/pid_file.rb
CHANGED
@@ -1,52 +1,35 @@
|
|
1
1
|
class RFlow
|
2
2
|
class PIDFile
|
3
|
-
|
3
|
+
private
|
4
|
+
attr_reader :path
|
4
5
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
def validate?
|
10
|
-
if current_process?
|
11
|
-
RFlow.logger.warn "Already running #{read.to_s}, not writing PID to file '#{to_s}'"
|
12
|
-
return nil
|
13
|
-
elsif running?
|
14
|
-
error_message = "Already running #{read.to_s}, possibly stale PID file '#{to_s}'"
|
15
|
-
RFlow.logger.error error_message
|
16
|
-
raise ArgumentError, error_message
|
17
|
-
elsif exist?
|
18
|
-
RFlow.logger.warn "Found stale PID #{read.to_s} in PID file '#{to_s}', removing"
|
19
|
-
unlink
|
20
|
-
end
|
21
|
-
true
|
6
|
+
public
|
7
|
+
def initialize(path)
|
8
|
+
@path = path
|
22
9
|
end
|
23
10
|
|
24
11
|
def read
|
25
|
-
return nil unless File.exist?
|
26
|
-
File.read(
|
12
|
+
return nil unless File.exist? path
|
13
|
+
File.read(path).to_i
|
27
14
|
end
|
28
15
|
|
29
|
-
def write(pid
|
16
|
+
def write(pid = $$)
|
30
17
|
return unless validate?
|
31
18
|
|
32
19
|
RFlow.logger.debug "Writing PID #{pid} file '#{to_s}'"
|
33
20
|
pid_fp = begin
|
34
|
-
|
35
|
-
File.open(
|
21
|
+
tmp_path = File.join(File.dirname(path), ".#{File.basename(path)}")
|
22
|
+
File.open(tmp_path, File::RDWR|File::CREAT|File::EXCL, 0644)
|
36
23
|
rescue Errno::EEXIST
|
37
24
|
retry
|
38
25
|
end
|
39
26
|
pid_fp.syswrite("#{pid}\n")
|
40
|
-
File.rename(pid_fp.path,
|
27
|
+
File.rename(pid_fp.path, path)
|
41
28
|
pid_fp.close
|
42
29
|
|
43
30
|
pid
|
44
31
|
end
|
45
32
|
|
46
|
-
def exist?
|
47
|
-
File.exist? pid_file_path
|
48
|
-
end
|
49
|
-
|
50
33
|
def running?
|
51
34
|
return false unless exist?
|
52
35
|
pid = read
|
@@ -57,14 +40,6 @@ class RFlow
|
|
57
40
|
nil
|
58
41
|
end
|
59
42
|
|
60
|
-
def current_process?
|
61
|
-
read == $$
|
62
|
-
end
|
63
|
-
|
64
|
-
def unlink
|
65
|
-
File.unlink(pid_file_path)
|
66
|
-
end
|
67
|
-
|
68
43
|
# unlinks a PID file at given if it contains the current PID still
|
69
44
|
# potentially racy without locking the directory (which is
|
70
45
|
# non-portable and may interact badly with other programs), but the
|
@@ -78,7 +53,32 @@ class RFlow
|
|
78
53
|
end
|
79
54
|
|
80
55
|
def to_s
|
81
|
-
File.expand_path(
|
56
|
+
File.expand_path(path)
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
def validate?
|
61
|
+
if current_process?
|
62
|
+
return nil
|
63
|
+
elsif running?
|
64
|
+
raise ArgumentError, "Process #{read.to_s} referenced in stale PID file '#{to_s}' still exists; probably attempting to run duplicate RFlow instances"
|
65
|
+
elsif exist?
|
66
|
+
RFlow.logger.warn "Found stale PID #{read.to_s} in PID file '#{to_s}', removing"
|
67
|
+
unlink
|
68
|
+
end
|
69
|
+
true
|
70
|
+
end
|
71
|
+
|
72
|
+
def exist?
|
73
|
+
File.exist? path
|
74
|
+
end
|
75
|
+
|
76
|
+
def current_process?
|
77
|
+
read == $$
|
78
|
+
end
|
79
|
+
|
80
|
+
def unlink
|
81
|
+
File.unlink(path)
|
82
82
|
end
|
83
83
|
end
|
84
84
|
end
|