rflow 1.0.0a1 → 1.0.0a2
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 +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
|