skylight 0.3.21 → 0.4.0.alpha1

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.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +0 -4
  3. data/ext/extconf.rb +92 -47
  4. data/ext/libskylight.yml +4 -4
  5. data/ext/skylight_native.c +248 -286
  6. data/lib/skylight.rb +19 -114
  7. data/lib/skylight/api.rb +1 -1
  8. data/lib/skylight/config.rb +176 -146
  9. data/lib/skylight/data/cacert.pem +717 -719
  10. data/lib/skylight/formatters/http.rb +1 -1
  11. data/lib/skylight/instrumenter.rb +28 -35
  12. data/lib/skylight/native.rb +58 -72
  13. data/lib/skylight/normalizers.rb +0 -1
  14. data/lib/skylight/normalizers/active_record/sql.rb +0 -4
  15. data/lib/skylight/probes/excon/middleware.rb +3 -1
  16. data/lib/skylight/probes/net_http.rb +3 -1
  17. data/lib/skylight/subscriber.rb +0 -4
  18. data/lib/skylight/trace.rb +189 -0
  19. data/lib/skylight/util.rb +10 -12
  20. data/lib/skylight/util/hostname.rb +17 -0
  21. data/lib/skylight/util/http.rb +33 -36
  22. data/lib/skylight/util/logging.rb +20 -1
  23. data/lib/skylight/util/multi_io.rb +21 -0
  24. data/lib/skylight/util/native_ext_fetcher.rb +83 -69
  25. data/lib/skylight/util/platform.rb +67 -0
  26. data/lib/skylight/util/ssl.rb +50 -0
  27. data/lib/skylight/version.rb +1 -1
  28. metadata +9 -34
  29. data/ext/rust_support/ruby.h +0 -93
  30. data/ext/skylight.h +0 -85
  31. data/ext/skylight.map +0 -4
  32. data/ext/test/extconf.rb +0 -18
  33. data/ext/test/skylight_native_test.c +0 -82
  34. data/ext/test/skylight_test.h +0 -20
  35. data/lib/skylight/formatters.rb +0 -6
  36. data/lib/skylight/messages.rb +0 -21
  37. data/lib/skylight/messages/error.rb +0 -15
  38. data/lib/skylight/messages/hello.rb +0 -13
  39. data/lib/skylight/messages/trace.rb +0 -179
  40. data/lib/skylight/messages/trace_envelope.rb +0 -19
  41. data/lib/skylight/metrics.rb +0 -9
  42. data/lib/skylight/metrics/ewma.rb +0 -69
  43. data/lib/skylight/metrics/meter.rb +0 -58
  44. data/lib/skylight/metrics/process_cpu_gauge.rb +0 -65
  45. data/lib/skylight/metrics/process_mem_gauge.rb +0 -34
  46. data/lib/skylight/util/conversions.rb +0 -9
  47. data/lib/skylight/util/queue.rb +0 -96
  48. data/lib/skylight/util/task.rb +0 -172
  49. data/lib/skylight/util/uniform_sample.rb +0 -63
  50. data/lib/skylight/worker.rb +0 -19
  51. data/lib/skylight/worker/builder.rb +0 -73
  52. data/lib/skylight/worker/collector.rb +0 -274
  53. data/lib/skylight/worker/connection.rb +0 -87
  54. data/lib/skylight/worker/connection_set.rb +0 -56
  55. data/lib/skylight/worker/embedded.rb +0 -24
  56. data/lib/skylight/worker/metrics_reporter.rb +0 -104
  57. data/lib/skylight/worker/server.rb +0 -336
  58. data/lib/skylight/worker/standalone.rb +0 -421
@@ -1,336 +0,0 @@
1
- require 'socket'
2
-
3
- module Skylight
4
- module Worker
5
- class Server
6
- LOCKFILE_PATH = 'SKYLIGHT_LOCKFILE_PATH'.freeze
7
- LOCKFILE_ENV_KEY = 'SKYLIGHT_LOCKFILE_FD'.freeze
8
- UDS_SRV_FD_KEY = 'SKYLIGHT_UDS_FD'.freeze
9
- KEEPALIVE_KEY = 'SKYLIGHT_KEEPALIVE'.freeze
10
-
11
- include Util::Logging
12
-
13
- attr_reader \
14
- :pid,
15
- :tick,
16
- :config,
17
- :keepalive,
18
- :lockfile_path,
19
- :sockfile_path,
20
- :last_status_update,
21
- :max_memory
22
-
23
- def initialize(config, lockfile, srv, lockfile_path)
24
- unless lockfile && srv
25
- raise ArgumentError, "lockfile and unix domain server socket are required"
26
- end
27
-
28
- @pid = Process.pid
29
- @run = true
30
- @tick = 1
31
- @socks = []
32
- @config = config
33
- @server = srv
34
- @lockfile = lockfile
35
- @collector = Collector.build(config)
36
- @metrics_reporter = @collector.metrics_reporter
37
- @keepalive = @config[:'agent.keepalive']
38
- @connections = ConnectionSet.new
39
- @lockfile_path = lockfile_path
40
- @sockfile_path = @config[:'agent.sockfile_path']
41
- @process_mem_gauge = Metrics::ProcessMemGauge.new
42
- @process_cpu_gauge = Metrics::ProcessCpuGauge.new
43
- @max_memory = @config[:'agent.max_memory']
44
- @booted_at = Util::Clock.absolute_secs
45
- end
46
-
47
- # Called from skylight.rb on require
48
- def self.boot
49
- fail = lambda do |msg|
50
- STDERR.puts msg
51
- exit 1
52
- end
53
-
54
- config = Config.load_from_env
55
-
56
- unless fd = ENV[LOCKFILE_ENV_KEY]
57
- fail.call "missing lockfile FD"
58
- end
59
-
60
- unless fd =~ /^\d+$/
61
- fail.call "invalid lockfile FD"
62
- end
63
-
64
- begin
65
- lockfile = IO.open(fd.to_i)
66
- rescue Exception => e
67
- fail.call "invalid lockfile FD: #{e.message}"
68
- end
69
-
70
- unless lockfile_path = ENV[LOCKFILE_PATH]
71
- fail.call "missing lockfile path"
72
- end
73
-
74
- unless config[:'agent.sockfile_path']
75
- fail.call "missing sockfile path"
76
- end
77
-
78
- srv = nil
79
- if fd = ENV[UDS_SRV_FD_KEY]
80
- srv = UNIXServer.for_fd(fd.to_i)
81
- end
82
-
83
- server = new(
84
- config,
85
- lockfile,
86
- srv,
87
- lockfile_path)
88
-
89
- server.run
90
- end
91
-
92
- def self.exec(cmd, config, lockfile, srv, lockfile_path)
93
- env = config.to_env
94
- env.merge!(
95
- STANDALONE_ENV_KEY => STANDALONE_ENV_VAL,
96
- LOCKFILE_PATH => lockfile_path,
97
- LOCKFILE_ENV_KEY => lockfile.fileno.to_s)
98
-
99
- if srv
100
- env[UDS_SRV_FD_KEY] = srv.fileno.to_s
101
- end
102
-
103
- opts = {}
104
- args = [env] + cmd + [opts]
105
-
106
- unless RUBY_VERSION < '1.9'
107
- [lockfile, srv].each do |io|
108
- next unless io
109
- fd = io.fileno.to_i
110
- opts[fd] = fd
111
- end
112
- end
113
-
114
- Kernel.exec(*args)
115
- end
116
-
117
- def run
118
- init
119
- work
120
- ensure
121
- cleanup
122
- end
123
-
124
- private
125
-
126
- def init
127
- # TODO: Not super ideal to always iterate here even if debug mode isn't
128
- # enabled, but it's not super perf critical. We will fix when we revamp
129
- # logging
130
- debug "initializing server; config=%s", config.to_env
131
-
132
- trap('TERM') { @run = false }
133
- trap('INT') { @run = false }
134
-
135
- # Register metrics
136
- @metrics_reporter.register("worker.memory", @process_mem_gauge)
137
- @metrics_reporter.register("worker.cpu", @process_cpu_gauge)
138
- @metrics_reporter.register("worker.uptime", lambda { Util::Clock.absolute_secs - @booted_at })
139
- @metrics_reporter.register("worker.ipc.open-connections", @connections.open_connections)
140
- @metrics_reporter.register("worker.ipc.throughput", @connections.throughput)
141
-
142
- info "starting skylight daemon"
143
- @collector.spawn
144
- end
145
-
146
- def work
147
- t { "server working" }
148
- @socks << @server
149
-
150
- now = Time.now.to_i
151
- next_sanity_check_at = now + tick
152
- had_client_at = now
153
- last_status_update = now
154
-
155
- trace "starting IO loop"
156
- begin
157
- # Wait for something to do
158
- r, _, _ = IO.select(@socks, [], [], tick)
159
-
160
- if r
161
- r.each do |sock|
162
- if sock == @server
163
- # If the server socket, accept
164
- # the incoming connection
165
- if client = accept
166
- connect(client)
167
- end
168
- else
169
- # Client socket, lookup the associated connection
170
- # state machine.
171
- unless conn = @connections[sock]
172
- # No associated connection, weird.. bail
173
- client_close(sock)
174
- next
175
- end
176
-
177
- begin
178
- # Pop em while we got em
179
- while msg = conn.read
180
- handle(msg)
181
- end
182
- rescue SystemCallError, EOFError
183
- client_close(sock)
184
- rescue IpcProtoError => e
185
- error "Server#work - IPC protocol exception: %s", e.message
186
- client_close(sock)
187
- end
188
- end
189
- end
190
- end
191
-
192
- now = Time.now.to_i
193
-
194
- if @socks.length > 1
195
- had_client_at = now
196
- end
197
-
198
- if keepalive < now - had_client_at
199
- info "no clients for #{keepalive} sec - shutting down"
200
- @run = false
201
- else
202
- if next_sanity_check_at <= now
203
- next_sanity_check_at = now + tick
204
- sanity_check
205
- end
206
-
207
- memory_usage = @process_mem_gauge.call
208
- if memory_usage > max_memory
209
- raise WorkerStateError, "Memory limit exceeded: #{memory_usage} (max: #{max_memory})"
210
- end
211
- end
212
-
213
- rescue SignalException => e
214
- error "Did not handle: #{e.class}"
215
- @run = false
216
- rescue WorkerStateError => e
217
- info "#{e.message} - shutting down"
218
- @run = false
219
- rescue Exception => e
220
- error "Loop exception: %s (%s)\n%s", e.message, e.class, e.backtrace.join("\n")
221
- @collector.send_exception(e)
222
- return false
223
- rescue Object => o
224
- error "Unknown object thrown: `%s`", o.to_s
225
- @collector.send_exception(o)
226
- return false
227
- end while @run
228
-
229
- true # Successful return
230
- ensure
231
- # Send a final metrics report
232
- @metrics_reporter.post_report
233
- end
234
-
235
- # Handles an incoming message. Will be instances from
236
- # the Messages namespace
237
- def handle(msg)
238
- case msg
239
- when nil
240
- return
241
- when Hello
242
- if msg.newer?
243
- info "newer version of agent deployed - restarting; curr=%s; new=%s", VERSION, msg.version
244
- reload(msg)
245
- end
246
- when Messages::TraceEnvelope, Error
247
- t { "received message" }
248
- @collector.submit(msg)
249
- when :unknown
250
- debug "received unknown message"
251
- else
252
- debug "recieved: %s", msg
253
- end
254
- end
255
-
256
- def reload(hello)
257
- # Close all client connections
258
- trace "closing all client connections"
259
- clients_close
260
-
261
- # Re-exec the process
262
- trace "re-exec"
263
- Server.exec(hello.cmd, @config, @lockfile, @server, lockfile_path)
264
- end
265
-
266
- def accept
267
- @server.accept_nonblock
268
- rescue Errno::EWOULDBLOCK, Errno::EAGAIN, Errno::ECONNABORTED
269
- end
270
-
271
- def connect(sock)
272
- trace "client accepted"
273
- @socks << sock
274
- @connections.add(sock)
275
- end
276
-
277
- def cleanup
278
- t { "server cleaning up" }
279
- # The lockfile is not deleted. There is no way to atomically ensure
280
- # that we are deleting the lockfile for the current process.
281
- cleanup_curr_sockfile
282
- close
283
- @lockfile.close
284
- end
285
-
286
- def close
287
- @server.close if @server
288
- clients_close
289
- end
290
-
291
- def clients_close
292
- @connections.socks.each do |sock|
293
- client_close(sock)
294
- end
295
- end
296
-
297
- def client_close(sock)
298
- trace "closing client connection; fd=%d", sock.fileno
299
- @connections.cleanup(sock)
300
- @socks.delete(sock)
301
- end
302
-
303
- def sockfile
304
- "#{sockfile_path}/skylight-#{pid}.sock"
305
- end
306
-
307
- def sockfile?
308
- File.exist?(sockfile)
309
- end
310
-
311
- def cleanup_curr_sockfile
312
- File.unlink(sockfile) rescue nil
313
- end
314
-
315
- def sanity_check
316
- if !File.exist?(lockfile_path)
317
- raise WorkerStateError, "lockfile gone"
318
- end
319
-
320
- pid = File.read(lockfile_path) rescue nil
321
-
322
- unless pid
323
- raise WorkerStateError, "could not read lockfile"
324
- end
325
-
326
- unless pid == Process.pid.to_s
327
- raise WorkerStateError, "lockfile points to different process"
328
- end
329
-
330
- unless sockfile?
331
- raise WorkerStateError, "sockfile gone"
332
- end
333
- end
334
- end
335
- end
336
- end
@@ -1,421 +0,0 @@
1
- require 'socket'
2
- require 'thread'
3
- require 'fileutils'
4
- require 'rbconfig'
5
-
6
- # TODO: Handle cool-off
7
- module Skylight
8
- module Worker
9
- # Handle to the agent subprocess. Manages creation, communication, and
10
- # shutdown. Lazily spawns a thread that handles writing messages to the
11
- # unix domain socket
12
- #
13
- class Standalone
14
- include Util::Logging
15
-
16
- # Locates skylight_native so that it can be included in the standalone agent startup command
17
- def self.locate_skylight_native
18
- $LOADED_FEATURES.find do |feature|
19
- return feature if feature =~ /skylight_native\.#{RbConfig::CONFIG['DLEXT']}/
20
- end
21
- end
22
-
23
- def self.build_subprocess_cmd
24
- # Native extension location
25
- native_path = locate_skylight_native
26
- native_dir = native_path ? File.dirname(native_path) : nil
27
-
28
- paths = [
29
- File.expand_path('../../..', __FILE__), # Ruby code root
30
- native_dir
31
- ].uniq.compact
32
-
33
- ret = [ RUBYBIN ]
34
- paths.each { |path| ret << "-I" << path }
35
- ret << File.expand_path('../../../skylight.rb', __FILE__) # The agent startup script
36
- ret
37
- end
38
-
39
- # Used to start the standalone agent as well as included in the hello message
40
- SUBPROCESS_CMD = build_subprocess_cmd
41
-
42
- # Used to handle starting the thread
43
- LOCK = Mutex.new
44
-
45
- attr_reader \
46
- :pid,
47
- :config,
48
- :lockfile,
49
- :keepalive,
50
- :max_spawns,
51
- :spawn_window,
52
- :sockfile_path
53
-
54
- def initialize(config, lockfile, server)
55
- @pid = nil
56
- @sock = nil
57
-
58
- unless config && lockfile && server
59
- raise ArgumentError, "all arguments are required"
60
- end
61
-
62
- @me = Process.pid
63
- @config = config
64
- @spawns = []
65
- @server = server
66
- @lockfile = lockfile
67
- @keepalive = config[:'agent.keepalive']
68
- @sockfile_path = config[:'agent.sockfile_path']
69
-
70
- # Should be configurable
71
- @max_spawns = 3
72
- @spawn_window = 5 * 60
73
-
74
- # Writer background processor will accept messages and write them to
75
- # the IPC socket
76
- @writer = build_queue
77
- end
78
-
79
- def spawn(*args)
80
- return if @pid
81
-
82
- if __spawn(*args)
83
- @writer.spawn
84
- true
85
- end
86
- end
87
-
88
- def submit(msg)
89
- unless msg.respond_to?(:encode) || msg.respond_to?(:native_serialize)
90
- raise ArgumentError, "message not encodable"
91
- end
92
-
93
- unless @pid
94
- t { "no pid, can't submit: #{msg.inspect}" }
95
- return
96
- end
97
-
98
- if @me != Process.pid
99
- handle_fork
100
- end
101
-
102
- @writer.submit(msg, @me)
103
- end
104
-
105
- # Shutdown any side task threads. Let the agent process die on it's own.
106
- def shutdown
107
- # TODO: implement
108
- @writer.submit(:SHUTDOWN)
109
- @writer.shutdown
110
- end
111
-
112
- # Shutdown any side task threads as well as the agent process
113
- def shutdown_all
114
- # TODO: implement
115
- shutdown
116
- end
117
-
118
- private
119
-
120
- def __spawn(timeout = 10)
121
- if timeout < 2
122
- raise ArgumentError, "at least 2 seconds required"
123
- end
124
-
125
- start = Time.now
126
-
127
- if @spawns.length >= @max_spawns
128
- if @spawn_window >= (start - @spawns.first)
129
- trace "too many spawns in window"
130
- return false
131
- end
132
-
133
- @spawns.unshift
134
- end
135
-
136
- @spawns << start
137
-
138
- check_permissions
139
-
140
- lockf = File.open lockfile, File::RDWR | File::CREAT
141
-
142
- spawn_worker(lockf)
143
-
144
- while timeout >= (Time.now - start)
145
- if pid = read_lockfile
146
- if sockfile?(pid)
147
- if sock = connect(pid)
148
- trace "connected to unix socket; pid=%s", pid
149
- write_msg(sock, build_hello)
150
- @sock = sock
151
- @pid = pid
152
- return true
153
- end
154
- end
155
- end
156
-
157
- sleep 0.1
158
- end
159
-
160
- trace "failed to spawn worker"
161
- return false
162
-
163
- ensure
164
- lockf.close rescue nil if lockf
165
- end
166
-
167
- def repair
168
- @sock.close rescue nil if @sock
169
-
170
- t { "repairing socket" }
171
-
172
- # Attempt to reconnect to the currently known agent PID. If the agent
173
- # is still healthy but is simply reloading itself, this should work
174
- # just fine.
175
- if sock = connect(@pid)
176
- t { "reconnected to worker" }
177
- @sock = sock
178
- # TODO: Should HELLO be sent again?
179
- return true
180
- end
181
-
182
- debug "failed to reconnect -- attempting worker respawn"
183
-
184
- # Attempt to respawn the agent process
185
- unless __spawn
186
- debug "could not respawn -- shutting down"
187
-
188
- @pid = nil
189
- @sock = nil
190
- return false
191
- end
192
-
193
- true
194
- end
195
-
196
- def writer_tick(msg)
197
- if :SHUTDOWN == msg
198
- trace "shuting down agent connection"
199
- @sock.close if @sock
200
- @pid = nil
201
-
202
- return false
203
- elsif msg
204
- return handle(msg)
205
- else
206
- begin
207
- @sock.read_nonblock(1)
208
- rescue Errno::EWOULDBLOCK, Errno::EAGAIN, Errno::EINTR
209
- rescue Exception => e
210
- trace "bad socket: #{e}"
211
- unless repair
212
- raise WorkerStateError, "could not repair connection to agent"
213
- end
214
- end
215
-
216
- return true
217
- end
218
- rescue WorkerStateError => e
219
- error "skylight shutting down: %s", e.message
220
- return false
221
- end
222
-
223
- def handle(msg)
224
- 2.times do
225
- unless sock = @sock
226
- return false unless repair
227
- sock = @sock
228
- end
229
-
230
- if write_msg(sock, msg)
231
- return true
232
- end
233
-
234
- @sock = nil
235
- sock.close rescue nil
236
-
237
- unless repair
238
- return false
239
- end
240
- end
241
-
242
- debug "could not handle message; msg=%s", msg.class
243
-
244
- false
245
- end
246
-
247
- def write_msg(sock, msg)
248
- t { "writing a #{msg.class} on the wire" }
249
- id = Messages::KLASS_TO_ID.fetch(msg.class)
250
- buf = msg.serialize
251
-
252
- frame = [ id, buf.bytesize ].pack("LL")
253
-
254
- write(sock, frame) && write(sock, buf)
255
- end
256
-
257
- SOCK_TIMEOUT_VAL = [ 0, 0.01 * 1_000_000 ].pack("l_2")
258
-
259
- # TODO: Handle configuring the socket with proper timeouts
260
- def connect(pid)
261
- sock = UNIXSocket.new(sockfile(pid)) rescue nil
262
- if sock
263
- sock.setsockopt Socket::SOL_SOCKET, Socket::SO_SNDTIMEO, SOCK_TIMEOUT_VAL
264
- sock
265
- end
266
- end
267
-
268
- def write(sock, msg, timeout = 5)
269
- msg = msg.to_s
270
- cnt = 10
271
-
272
- begin
273
- while true
274
- res = sock.write_nonblock(msg)
275
-
276
- if res == msg.bytesize
277
- return true
278
- elsif res > 0
279
- msg = msg.byteslice(res..-1)
280
- cnt = 10
281
- else
282
- if 0 <= (cnt -= 1)
283
- t { "write failed -- max attempts" }
284
- return false
285
- end
286
- end
287
- end
288
- rescue Errno::EAGAIN, Errno::EWOULDBLOCK
289
- _, socks, = IO.select([], [sock], [], timeout)
290
- unless socks == [sock]
291
- t { "write timed out" }
292
- return false
293
- end
294
- retry
295
- rescue Errno::EINTR
296
- raise
297
- rescue SystemCallError => e
298
- t { fmt "write failed; err=%s", e.class }
299
- return false
300
- end
301
- end
302
-
303
- # Spawn the worker process.
304
- def spawn_worker(f)
305
- pid = fork do
306
- # Note: By default, Ruby will finalize C objects inside the fork. Because those C objects
307
- # are shared with the parent, this can cause database connections to disconnect in the
308
- # parent process. We need to double-fork for proper semantics, so we disable the GC and
309
- # exit! to avoid finalizing shared handles.
310
- #
311
- # We should continue to look for alternate solutions, and to determine whether there is
312
- # still a possible race between the fork and the GC disabling.
313
- ::GC.disable
314
- ::Process.setsid
315
- exit! if fork
316
-
317
- # Acquire exclusive file lock, exit otherwise
318
- unless f.flock(File::LOCK_EX | File::LOCK_NB)
319
- exit! 1
320
- end
321
-
322
- f.truncate(0)
323
-
324
- # Lock acquired, cleanup old sock files
325
- Dir["#{sockfile_path}/skylight-*.sock"].each do |sf|
326
- File.unlink(sf) rescue nil
327
- end
328
-
329
- pid = Process.pid.to_s
330
-
331
- # Write the pid
332
- f.write(pid)
333
- f.flush
334
-
335
- sf = sockfile(pid)
336
- File.unlink(sf) rescue nil
337
-
338
- t { fmt "opening a new socket; %s", sf }
339
- srv = UNIXServer.new(sf)
340
-
341
- unless ENV[TRACE_ENV_KEY]
342
- null = File.open "/dev/null", File::RDWR
343
- STDIN.reopen null
344
- STDOUT.reopen null
345
- STDERR.reopen null
346
- end
347
-
348
- # Cleanup the ENV
349
- ENV['RUBYOPT'] = nil
350
-
351
- @server.exec(SUBPROCESS_CMD, @config, f, srv, lockfile)
352
- end
353
-
354
- Process.detach(pid)
355
- end
356
-
357
- # If the process was forked, create a new queue and restart the worker
358
- def handle_fork
359
- LOCK.synchronize do
360
- if @me != Process.pid
361
- trace "process forked; recovering"
362
- # Update the current process ID
363
- @me = Process.pid
364
-
365
- # Deal w/ the inherited socket
366
- @sock.close rescue nil if @sock
367
- @sock = nil
368
-
369
- @writer = build_queue
370
- @writer.spawn
371
- end
372
- end
373
- end
374
-
375
- def check_permissions
376
- lockfile_root = File.dirname(lockfile)
377
-
378
- FileUtils.mkdir_p lockfile_root
379
- FileUtils.mkdir_p sockfile_path
380
-
381
- if File.exist?(lockfile)
382
- if !FileTest.writable?(lockfile)
383
- raise WorkerStateError, "`#{lockfile}` not writable. Please set agent.lockfile or agent.sockfile_path in your config to a writable path."
384
- end
385
- else
386
- if !FileTest.writable?(lockfile_root)
387
- raise WorkerStateError, "`#{lockfile_root}` not writable. Please set agent.lockfile or agent.sockfile_path in your config to a writable path."
388
- end
389
- end
390
-
391
- unless FileTest.writable?(sockfile_path)
392
- raise WorkerStateError, "`#{sockfile_path}` not writable. Please set agent.sockfile_path in your config to a writable path."
393
- end
394
- end
395
-
396
- def build_hello
397
- Messages::Hello.build(VERSION, SUBPROCESS_CMD)
398
- end
399
-
400
- def build_queue
401
- Util::Task.new(100, 1) { |m| writer_tick(m) }
402
- end
403
-
404
- def read_lockfile
405
- pid = File.read(lockfile) rescue nil
406
- if pid =~ /^\d+$/
407
- pid.to_i
408
- end
409
- end
410
-
411
- def sockfile(pid)
412
- "#{sockfile_path}/skylight-#{pid}.sock"
413
- end
414
-
415
- def sockfile?(pid)
416
- File.exist?(sockfile(pid))
417
- end
418
-
419
- end
420
- end
421
- end