lib/config_skeleton.rb in config_skeleton-0.1.0 vs lib/config_skeleton.rb in config_skeleton-0.2.6

- old
+ new

@@ -140,10 +140,22 @@ class Error < StandardError; end # If you get this, someone didn't read the documentation. class NotImplementedError < Error; end + # It is useful for consumers to manually request a config regen. An instance + # of this class is made via the regen_notifier method. + class ConfigRegenNotifier + def initialize(io_write) + @io_write = io_write + end + + def trigger_regen + @io_write << "." + end + end + # Declare a file watch on all instances of the config generator. # # When you're looking to watch a file whose path is well-known and never-changing, you # can declare the watch in the class. # @@ -186,12 +198,24 @@ logger.info("SIGHUP") { "received SIGHUP, triggering config regeneration" } regenerate_config(force_reload: true) end initialize_config_skeleton_metrics + @trigger_regen_r, @trigger_regen_w = IO.pipe end + # Expose the write pipe which can be written to to trigger a config + # regeneration with a forced reload; a similar mechanism is used for + # shutdown but in that case writes are managed internally. + # + # Usage: config.regen_notifier.trigger_regen + # + # @return [ConfigRegenNotifier] + def regen_notifier + @regen_notifier ||= ConfigRegenNotifier.new(@trigger_regen_w) + end + # Set the config generator running. # # Does the needful to generate configs and reload the server. Typically # never returns, unless you send the process a `SIGTERM`/`SIGINT`. # @@ -207,17 +231,29 @@ logger.debug(logloc) { "notifier fd is #{notifier.to_io.inspect}" } @terminate_r, @terminate_w = IO.pipe loop do - if ios = IO.select([notifier.to_io, @terminate_r], [], [], sleep_duration.tap { |d| logger.debug(logloc) { "Sleeping for #{d} seconds" } }) + if ios = IO.select( + [notifier.to_io, @terminate_r, @trigger_regen_r], + [], [], + sleep_duration.tap { |d| logger.debug(logloc) { "Sleeping for #{d} seconds" } } + ) if ios.first.include?(notifier.to_io) logger.debug(logloc) { "inotify triggered" } notifier.process regenerate_config(force_reload: true) elsif ios.first.include?(@terminate_r) logger.debug(logloc) { "triggered by termination pipe" } break + elsif ios.first.include?(@trigger_regen_r) + # we want to wait until everything in the backlog is read + # before proceeding so we don't run out of buffer memory + # for the pipe + while @trigger_regen_r.read_nonblock(20, nil, exception: false) != :wait_readable; end + + logger.debug(logloc) { "triggered by regen pipe" } + regenerate_config(force_reload: true) else logger.error(logloc) { "Mysterious return from select: #{ios.inspect}" } end else logger.debug(logloc) { "triggered by timeout" }