<commit_before>require 'fcntl'
require 'optparse'
require 'pp'
require 'set'
require 'socket'
require 'tmpdir'
require 'yaml'
require 'shellwords'

require 'einhorn/third/little-plugger'

module Einhorn
  extend Third::LittlePlugger
  module Plugins; end

  def self.plugins_send(sym, *args)
    plugins.values.each do |plugin|
      plugin.send(sym, *args) if plugin.respond_to? sym
    end
  end

  module AbstractState
    def default_state; raise NotImplementedError.new('Override in extended modules'); end
    def state; @state ||= default_state; end
    def state=(v); @state = v; end
    def dumpable_state; state; end

    def method_missing(name, *args)
      if (name.to_s =~ /(.*)=$/) && state.has_key?($1.to_sym)
        state.send(:[]=, $1.to_sym, *args)
      elsif state.has_key?(name)
        state[name]
      else
        ds = default_state
        if ds.has_key?(name)
          ds[name]
        else
          super
        end
      end
    end
  end

  module State
    extend AbstractState

    # WARNING: Don't change or remove these variables without thinking
    # about backwards/forwards compatibility for upgrades/downgrades
    def self.default_state
      {
        :children => {},
        :config => {:number => 1, :backlog => 100, :seconds => 1},
        :versions => {},
        :version => 0,
        :sockets => {},
        :orig_cmd => nil,
        :bind => [],
        :bind_fds => [],
        :cmd => nil,
        :script_name => nil,
        :respawn => true,
        :upgrading => false,
        :smooth_upgrade => false,
        :reloading_for_upgrade => false,
        :path => nil,
        :cmd_name => nil,
        :verbosity => 1,
        :generation => 0,
        :last_spinup => nil,
        :ack_mode => {:type => :timer, :timeout => 1},
        :kill_children_on_exit => false,
        :command_socket_as_fd => false,
        :socket_path => nil,
        :pidfile => nil,
        :lockfile => nil,
        :consecutive_deaths_before_ack => 0,
        :last_upgraded => nil,
        :nice => {:master => nil, :worker => nil, :renice_cmd => '/usr/bin/renice'},
        :reexec_commandline => nil,
        :drop_environment_variables => [],
      }
    end

    def self.dumpable_state
      dump = state
      dump[:reloading_for_preload_upgrade] = dump[:reloading_for_upgrade]
      dump
    end
  end

  module TransientState
    extend AbstractState
    def self.default_state
      {
        :whatami => :master,
        :preloaded => false,
        :script_name => nil,
        :argv => [],
        :environ => {},
        :has_outstanding_spinup_timer => false,
        :stateful => nil,
        # Holds references so that the GC doesn't go and close your sockets.
        :socket_handles => Set.new
      }
    end
  end

  def self.restore_state(state)
    parsed = YAML.load(state)
    updated_state, message = update_state(Einhorn::State, "einhorn", parsed[:state])
    Einhorn::State.state = updated_state
    Einhorn::Event.restore_persistent_descriptors(parsed[:persistent_descriptors])
    plugin_messages = update_plugin_states(parsed[:plugins])
    # Do this after setting state so verbosity is right
    Einhorn.log_info("Using loaded state: #{parsed.inspect}")
    Einhorn.log_info(message) if message
    plugin_messages.each {|msg| Einhorn.log_info(msg)}
  end

  def self.update_state(store, store_name, old_state)
    # TODO: handle format updates somehow? (probably need to write
    # special-case code for each)
    updated_state = old_state.dup
    if store == Einhorn::State
      if updated_state.include?(:reloading_for_preload_upgrade) &&
          !updated_state.include?(:reloading_for_upgrade)
        updated_state[:reloading_for_upgrade] = updated_state.delete(:reloading_for_preload_upgrade)
      end
    end

    default = store.default_state
    added_keys = default.keys - old_state.keys
    deleted_keys = old_state.keys - default.keys
    return [updated_state, nil] if added_keys.length == 0 && deleted_keys.length == 0

    added_keys.each {|key| updated_state[key] = default[key]}
    deleted_keys.each {|key| updated_state.delete(key)}

    message = []
    message << "adding default values for #{added_keys.inspect}"
    message << "deleting values for #{deleted_keys.inspect}"
    message = "State format for #{store_name} has changed: #{message.join(', ')}"

    # Can't print yet, since state hasn't been set, so we pass along the message.
    [updated_state, message]
  end

  def self.update_plugin_states(states)
    plugin_messages = []
    (states || {}).each do |name, plugin_state|
      plugin = Einhorn.plugins[name]
      unless plugin && plugin.const_defined?(:State)
        plugin_messages << "No state defined in this version of the #{name} " +
          "plugin; dropping values for keys #{plugin_state.keys.inspect}"
        next
      end

      updated_state, plugin_message = update_state(plugin::State, "plugin #{name}", plugin_state)
      plugin_messages << plugin_message if plugin_message
      plugin::State.state = updated_state
    end
    plugin_messages
  end

  def self.print_state
    log_info(Einhorn::State.state.pretty_inspect)
  end

  def self.bind(addr, port, flags)<commit_msg>Add a log message if we have to upgrade config<commit_after>require 'fcntl'
require 'optparse'
require 'pp'
require 'set'
require 'socket'
require 'tmpdir'
require 'yaml'
require 'shellwords'

require 'einhorn/third/little-plugger'

module Einhorn
  extend Third::LittlePlugger
  module Plugins; end

  def self.plugins_send(sym, *args)
    plugins.values.each do |plugin|
      plugin.send(sym, *args) if plugin.respond_to? sym
    end
  end

  module AbstractState
    def default_state; raise NotImplementedError.new('Override in extended modules'); end
    def state; @state ||= default_state; end
    def state=(v); @state = v; end
    def dumpable_state; state; end

    def method_missing(name, *args)
      if (name.to_s =~ /(.*)=$/) && state.has_key?($1.to_sym)
        state.send(:[]=, $1.to_sym, *args)
      elsif state.has_key?(name)
        state[name]
      else
        ds = default_state
        if ds.has_key?(name)
          ds[name]
        else
          super
        end
      end
    end
  end

  module State
    extend AbstractState

    # WARNING: Don't change or remove these variables without thinking
    # about backwards/forwards compatibility for upgrades/downgrades
    def self.default_state
      {
        :children => {},
        :config => {:number => 1, :backlog => 100, :seconds => 1},
        :versions => {},
        :version => 0,
        :sockets => {},
        :orig_cmd => nil,
        :bind => [],
        :bind_fds => [],
        :cmd => nil,
        :script_name => nil,
        :respawn => true,
        :upgrading => false,
        :smooth_upgrade => false,
        :reloading_for_upgrade => false,
        :path => nil,
        :cmd_name => nil,
        :verbosity => 1,
        :generation => 0,
        :last_spinup => nil,
        :ack_mode => {:type => :timer, :timeout => 1},
        :kill_children_on_exit => false,
        :command_socket_as_fd => false,
        :socket_path => nil,
        :pidfile => nil,
        :lockfile => nil,
        :consecutive_deaths_before_ack => 0,
        :last_upgraded => nil,
        :nice => {:master => nil, :worker => nil, :renice_cmd => '/usr/bin/renice'},
        :reexec_commandline => nil,
        :drop_environment_variables => [],
      }
    end

    def self.dumpable_state
      dump = state
      dump[:reloading_for_preload_upgrade] = dump[:reloading_for_upgrade]
      dump
    end
  end

  module TransientState
    extend AbstractState
    def self.default_state
      {
        :whatami => :master,
        :preloaded => false,
        :script_name => nil,
        :argv => [],
        :environ => {},
        :has_outstanding_spinup_timer => false,
        :stateful => nil,
        # Holds references so that the GC doesn't go and close your sockets.
        :socket_handles => Set.new
      }
    end
  end

  def self.restore_state(state)
    parsed = YAML.load(state)
    updated_state, message = update_state(Einhorn::State, "einhorn", parsed[:state])
    Einhorn::State.state = updated_state
    Einhorn::Event.restore_persistent_descriptors(parsed[:persistent_descriptors])
    plugin_messages = update_plugin_states(parsed[:plugins])
    # Do this after setting state so verbosity is right
    Einhorn.log_info("Using loaded state: #{parsed.inspect}")
    Einhorn.log_info(message) if message
    plugin_messages.each {|msg| Einhorn.log_info(msg)}
  end

  def self.update_state(store, store_name, old_state)
    # TODO: handle format updates somehow? (probably need to write
    # special-case code for each)
    message = []
    updated_state = old_state.dup
    if store == Einhorn::State
      if updated_state.include?(:reloading_for_preload_upgrade) &&
          !updated_state.include?(:reloading_for_upgrade)
        updated_state[:reloading_for_upgrade] = updated_state.delete(:reloading_for_preload_upgrade)
        message << "upgraded :reloading_for_preload_upgrade to :reloading_for_upgrade"
      end
    end

    default = store.default_state
    added_keys = default.keys - old_state.keys
    deleted_keys = old_state.keys - default.keys
    return [updated_state, message.first] if added_keys.length == 0 && deleted_keys.length == 0

    added_keys.each {|key| updated_state[key] = default[key]}
    deleted_keys.each {|key| updated_state.delete(key)}

    message << "adding default values for #{added_keys.inspect}"
    message << "deleting values for #{deleted_keys.inspect}"
    message = "State format for #{store_name} has changed: #{message.join(', ')}"

    # Can't print yet, since state hasn't been set, so we pass along the message.
    [updated_state, message]
  end

  def self.update_plugin_states(states)
    plugin_messages = []
    (states || {}).each do |name, plugin_state|
      plugin = Einhorn.plugins[name]
      unless plugin && plugin.const_defined?(:State)
        plugin_messages << "No state defined in this version of the #{name} " +
          "plugin; dropping values for keys #{plugin_state.keys.inspect}"
        next
      end

      updated_state, plugin_message = update_state(plugin::State, "plugin #{name}", plugin_state)
      plugin_messages << plugin_message if plugin_message
      plugin::State.state = updated_state
    end
    plugin_messages
  end

  def self.print_state
    log_info(Einhorn::State.state.pretty_inspect)
  end

  def self.bind(addr, port, flags)
{'fraction_non_alphanumeric': 0.11520257478227944, 'fraction_numerical': 0.0026505111700113595, 'mean_word_length': 2.826512133285042, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 95, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2795052', 'n_tokens_mistral': 3316, 'n_tokens_neox': 3238, 'n_words': 801}