class Debloater::Index

Attributes

name[R]

Public Class Methods

each(conn) { |new(conn, name: d, sql: d)| ... } click to toggle source
# File lib/debloater/index.rb, line 18
def self.each(conn)
  data = conn.exec_params %{
    select * from pg_indexes where schemaname = $1
  }, ['public']
  data.sort_by { |d| 
    d['indexname'] 
  }.each { |d| 
    next if d['indexname'] =~ /idx_debloat/
    yield new(conn, name: d['indexname'], sql: d['indexdef'])
  }
rescue PG::Error => e
  _fatal e, msg: 'Could not list indexes'
end
new(conn, name:, sql:) click to toggle source

conn: connection object method: the function used to obtain index information name: the index name

# File lib/debloater/index.rb, line 10
def initialize(conn, name:, sql:)
  @conn = conn
  @name = name
  @sql = sql
end

Public Instance Methods

bloat() click to toggle source
# File lib/debloater/index.rb, line 48
def bloat
  @bloat ||= size * (1 - density)
end
debloat!() click to toggle source
# File lib/debloater/index.rb, line 58
def debloat!
  _log "Old index definition:"
  _log @sql

  [
    'DROP INDEX IF EXISTS idx_debloat_new',
    @sql.
      sub(/CONCURRENTLY/i, '').
      sub(/CREATE\s+(UNIQUE\s+)?INDEX/i, 'CREATE \1INDEX CONCURRENTLY').
      sub(@name, 'idx_debloat_new'),
    %{
      BEGIN;
      ALTER INDEX #{@name} RENAME TO idx_debloat_old;
      ALTER INDEX idx_debloat_new RENAME TO #{@name};
      DROP INDEX idx_debloat_old;
      COMMIT;
    },
  ].each do |sql|
    begin
      _log "\t#{sql}"
      @conn.exec sql
    rescue PG::TRDeadlockDetected => e
      _log "Deadlock encountered. Press enter to retry, ^C to abort."
      IO.console.gets
      @conn.exec 'ROLLBACK;'
      retry
    rescue PG::DependentObjectsStillExist => e
      _log "Could not debloat '#{@name}', skipping. Details:"
      _log e.message
    rescue PG::Error => e
      _fatal e, msg: "Failure during index debloating, you may want to delete the temporary index 'idx_debloat_new' manually."
    end
  end
end
density() click to toggle source
# File lib/debloater/index.rb, line 43
def density
  @density ||= 0.01 * _metadata['avg_leaf_density'].to_f
end
pkey?() click to toggle source
# File lib/debloater/index.rb, line 33
def pkey?
  @name =~ /pkey/
end
size() click to toggle source
# File lib/debloater/index.rb, line 53
def size
  @size ||= _metadata['index_size'].to_i / 1024**2
end
valid?() click to toggle source
# File lib/debloater/index.rb, line 38
def valid?
  !_metadata.nil?
end

Private Instance Methods

_metadata() click to toggle source
# File lib/debloater/index.rb, line 97
def _metadata
  return @_metadata if defined?(@_metadata)

  @_metadata = @conn.exec(%{
    select * from #{@conn.statindex_method}('#{@name}');
  }).to_a.first
rescue PG::InvalidName
  _log "Invalid named index '#{@name}'"
  @_metadata = nil
rescue PG::Error => e
  if e.message =~ /is not a btree index/
    _log "Invalid non-Btree index '#{@name}'"
    @_metadata = nil
  else
    _fatal e, msg: "Could not fetch metadata for index '#{@name}'"
  end
end