<commit_before>require 'json'
require 'db'

module Jetpants
  
  # a Pool represents a group of database instances (Jetpants::DB objects).
  #
  # The default implementation assumes that a Pool contains:
  # * 1 master
  # * 0 or more slaves, falling into one of these categories:
  #   * active slaves (actively taking production read queries)
  #   * standby slaves (for HA, promotable if a master or active slave fails + used to clone new replacements)
  #   * backup slaves (dedicated for backups and background jobs, never put into prod, potentially different hardware spec)
  #
  # Plugins may of course override this extensively, to support different
  # topologies, such as master-master trees.
  #
  # Many of these methods are only useful in conjunction with an asset-tracker /
  # configuration-generator plugin
  class Pool
    include CallbackHandler

    # human-readable String name of pool
    attr_reader   :name
    
    # Jetpants::DB object that is the pool's master
    attr_reader   :master
    
    # Array of strings containing other equivalent names for this pool
    attr_reader   :aliases
    
    # Can be used to store a name that refers to just the active_slaves, for
    # instance if your framework isn't smart enough to know about master/slave
    # relationships.  Safe to leave as nil otherwise. Has no effect in Jetpants,
    # but an asset tracker / config generator plugin may include this in the
    # generated config file.
    attr_accessor :slave_name
    
    # Hash mapping DB object => weight, for active (read) slaves. Default weight
    # is 100. Safe to leave at default if your app framework doesn't support
    # different weights for individual read slaves. Weights have no effect inside
    # Jetpants, but any asset tracker / config generator plugin can carry them
    # through to the config file.
    attr_reader   :active_slave_weights
    
    # If the master also receives read queries, this stores its weight. Set to 0
    # if the master does not receive read queries (which is the default). This
    # has no effect inside of Jetpants, but can be used by an asset tracker /
    # config generator plugin to carry the value through to the config file.
    attr_accessor :master_read_weight
    
    def initialize(name, master)
      @name = name
      @slave_name = false
      @aliases = []
      @master = master.to_db
      @master_read_weight = 0
      @active_slave_weights = {}
      @tables = nil
    end
    
    # Returns all slaves, or pass in :active, :standby, or :backup to receive slaves
    # just of a particular type
    def slaves(type=false)
      case type
      when :active  then active_slaves
      when :standby then standby_slaves
      when :backup  then backup_slaves
      when false    then @master.slaves
      else []
      end
    end
    
    # Returns an array of Jetpants::DB objects.
    # Active slaves are ones that receive read queries from your application.
    def active_slaves
      @master.slaves.select {|sl| @active_slave_weights[sl]}
    end
    
    # Returns an array of Jetpants::DB objects.
    # Standby slaves do not receive queries from your application. These are for high availability.
    # They can be turned into active slaves or even the master, and can also be used for cloning
    # additional slaves.
    def standby_slaves
      @master.slaves.reject {|sl| @active_slave_weights[sl] || sl.for_backups?}
    end
    
    # Returns an array of Jetpants::DB objects.
    # Backup slaves are never promoted to active or master. They are for dedicated backup purposes.
    # They may be a different/cheaper hardware spec than other slaves.
    def backup_slaves
      @master.slaves.reject {|sl| @active_slave_weights[sl] || !sl.for_backups?}
    end
    
    # returns a flat array of all Jetpants::DB objects in the pool: the master and
    # all slaves of all types.
    def nodes
      [master, slaves].flatten.compact
    end

    # Look at a database in the pool (preferably a standby slave, but will check
    # active slave or master if nothing else is available) and retrieve a list of
    # tables, detecting their schema
    def probe_tables
      master.probe
      db = standby_slaves.last || active_slaves.last || master
      if db && db.running?
        output "Probing tables via #{db}"
      else
        output "Warning: unable to probe tables"
        return
      end
      
      @tables = []
      sql = "SHOW TABLES"
      db.query_return_array(sql).each do |tbl|
        table_name = tbl.values.first
        @tables << db.detect_table_schema(table_name)
      end
    end
    
    # Returns a list of table objects for this pool
    def tables
      self.probe_tables unless @tables
      @tables
    end

    # Queries whether a pool has a table with a given name
    # note that this is the string name of the table and not an object
    def has_table?(table)
      tables.map{|tb| tb.to_s}.include?(table)
    end

    # Retrieve the table object for a given table name
    def get_table(table)
      raise "Pool #{self} does not have table #{table}" unless has_table? table

      @tables.select{|tb| tb.to_s == table}.first
    end

    # Informs Jetpants that slave_db is an active slave. Potentially used by 
    # plugins, such as in Topology at start-up time.
    def has_active_slave(slave_db, weight=100)
      slave_db = slave_db.to_db
      raise "Attempt to mark a DB as its own active slave" if slave_db == @master
      @active_slave_weights[slave_db] = weight
    end
    
    # Turns a standby slave into an active slave, giving it the specified read weight.
    # Syncs the pool's configuration afterwards. It's up to your asset tracker plugin to
    # actually do something with this information.
    def mark_slave_active(slave_db, weight=100)
      raise "Attempt to make a backup slave be an active slave" if slave_db.for_backups?
      has_active_slave slave_db, weight
      sync_configuration
    end
    
    # Turns an active slave into a standby slave. Syncs the pool's configuration afterwards.
    # It's up to your asset tracker plugin to actually do something with this information.
    def mark_slave_standby(slave_db)
      slave_db = slave_db.to_db<commit_msg>Use shorthand<commit_after>require 'json'
require 'db'

module Jetpants
  
  # a Pool represents a group of database instances (Jetpants::DB objects).
  #
  # The default implementation assumes that a Pool contains:
  # * 1 master
  # * 0 or more slaves, falling into one of these categories:
  #   * active slaves (actively taking production read queries)
  #   * standby slaves (for HA, promotable if a master or active slave fails + used to clone new replacements)
  #   * backup slaves (dedicated for backups and background jobs, never put into prod, potentially different hardware spec)
  #
  # Plugins may of course override this extensively, to support different
  # topologies, such as master-master trees.
  #
  # Many of these methods are only useful in conjunction with an asset-tracker /
  # configuration-generator plugin
  class Pool
    include CallbackHandler

    # human-readable String name of pool
    attr_reader   :name
    
    # Jetpants::DB object that is the pool's master
    attr_reader   :master
    
    # Array of strings containing other equivalent names for this pool
    attr_reader   :aliases
    
    # Can be used to store a name that refers to just the active_slaves, for
    # instance if your framework isn't smart enough to know about master/slave
    # relationships.  Safe to leave as nil otherwise. Has no effect in Jetpants,
    # but an asset tracker / config generator plugin may include this in the
    # generated config file.
    attr_accessor :slave_name
    
    # Hash mapping DB object => weight, for active (read) slaves. Default weight
    # is 100. Safe to leave at default if your app framework doesn't support
    # different weights for individual read slaves. Weights have no effect inside
    # Jetpants, but any asset tracker / config generator plugin can carry them
    # through to the config file.
    attr_reader   :active_slave_weights
    
    # If the master also receives read queries, this stores its weight. Set to 0
    # if the master does not receive read queries (which is the default). This
    # has no effect inside of Jetpants, but can be used by an asset tracker /
    # config generator plugin to carry the value through to the config file.
    attr_accessor :master_read_weight
    
    def initialize(name, master)
      @name = name
      @slave_name = false
      @aliases = []
      @master = master.to_db
      @master_read_weight = 0
      @active_slave_weights = {}
      @tables = nil
    end
    
    # Returns all slaves, or pass in :active, :standby, or :backup to receive slaves
    # just of a particular type
    def slaves(type=false)
      case type
      when :active  then active_slaves
      when :standby then standby_slaves
      when :backup  then backup_slaves
      when false    then @master.slaves
      else []
      end
    end
    
    # Returns an array of Jetpants::DB objects.
    # Active slaves are ones that receive read queries from your application.
    def active_slaves
      @master.slaves.select {|sl| @active_slave_weights[sl]}
    end
    
    # Returns an array of Jetpants::DB objects.
    # Standby slaves do not receive queries from your application. These are for high availability.
    # They can be turned into active slaves or even the master, and can also be used for cloning
    # additional slaves.
    def standby_slaves
      @master.slaves.reject {|sl| @active_slave_weights[sl] || sl.for_backups?}
    end
    
    # Returns an array of Jetpants::DB objects.
    # Backup slaves are never promoted to active or master. They are for dedicated backup purposes.
    # They may be a different/cheaper hardware spec than other slaves.
    def backup_slaves
      @master.slaves.reject {|sl| @active_slave_weights[sl] || !sl.for_backups?}
    end
    
    # returns a flat array of all Jetpants::DB objects in the pool: the master and
    # all slaves of all types.
    def nodes
      [master, slaves].flatten.compact
    end

    # Look at a database in the pool (preferably a standby slave, but will check
    # active slave or master if nothing else is available) and retrieve a list of
    # tables, detecting their schema
    def probe_tables
      master.probe
      db = standby_slaves.last || active_slaves.last || master
      if db && db.running?
        output "Probing tables via #{db}"
      else
        output "Warning: unable to probe tables"
        return
      end
      
      @tables = []
      sql = "SHOW TABLES"
      db.query_return_array(sql).each do |tbl|
        table_name = tbl.values.first
        @tables << db.detect_table_schema(table_name)
      end
    end
    
    # Returns a list of table objects for this pool
    def tables
      self.probe_tables unless @tables
      @tables
    end

    # Queries whether a pool has a table with a given name
    # note that this is the string name of the table and not an object
    def has_table?(table)
      tables.map(&:to_s).include?(table)
    end

    # Retrieve the table object for a given table name
    def get_table(table)
      raise "Pool #{self} does not have table #{table}" unless has_table? table

      @tables.select{|tb| tb.to_s == table}.first
    end

    # Informs Jetpants that slave_db is an active slave. Potentially used by 
    # plugins, such as in Topology at start-up time.
    def has_active_slave(slave_db, weight=100)
      slave_db = slave_db.to_db
      raise "Attempt to mark a DB as its own active slave" if slave_db == @master
      @active_slave_weights[slave_db] = weight
    end
    
    # Turns a standby slave into an active slave, giving it the specified read weight.
    # Syncs the pool's configuration afterwards. It's up to your asset tracker plugin to
    # actually do something with this information.
    def mark_slave_active(slave_db, weight=100)
      raise "Attempt to make a backup slave be an active slave" if slave_db.for_backups?
      has_active_slave slave_db, weight
      sync_configuration
    end
    
    # Turns an active slave into a standby slave. Syncs the pool's configuration afterwards.
    # It's up to your asset tracker plugin to actually do something with this information.
    def mark_slave_standby(slave_db)
      slave_db = slave_db.to_db
{'fraction_non_alphanumeric': 0.05810225997756051, 'fraction_numerical': 0.002083667254367687, 'mean_word_length': 2.809218559218559, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 5, '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': '1161909', 'n_tokens_mistral': 3478, 'n_tokens_neox': 3385, 'n_words': 1509}