Class: Dexter::Indexer

Inherits:
Object
  • Object
show all
Includes:
Logging
Defined in:
lib/dexter/indexer.rb

Instance Method Summary collapse

Methods included from Logging

#log

Constructor Details

#initialize(options) ⇒ Indexer

Returns a new instance of Indexer.



5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# File 'lib/dexter/indexer.rb', line 5

def initialize(options)
  @create = options[:create]
  @log_level = options[:log_level]
  @exclude_tables = options[:exclude]
  @include_tables = Array(options[:include].split(",")) if options[:include]
  @log_sql = options[:log_sql]
  @log_explain = options[:log_explain]
  @min_time = options[:min_time] || 0
  @min_calls = options[:min_calls] || 0
  @analyze = options[:analyze]
  @options = options

  create_extension unless extension_exists?
  execute("SET lock_timeout = '5s'")
end

Instance Method Details

#process_queries(queries) ⇒ Object



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/dexter/indexer.rb', line 27

def process_queries(queries)
  # reset hypothetical indexes
  reset_hypothetical_indexes

  tables = Set.new(database_tables)

  if @include_tables
    include_set = Set.new(@include_tables)
    tables.keep_if { |t| include_set.include?(t) || include_set.include?(t.split(".")[-1]) }
  end

  if @exclude_tables.any?
    exclude_set = Set.new(@exclude_tables)
    tables.delete_if { |t| exclude_set.include?(t) || exclude_set.include?(t.split(".")[-1]) }
  end

  # map tables without schema to schema
  no_schema_tables = {}
  search_path_index = Hash[search_path.map.with_index.to_a]
  tables.group_by { |t| t.split(".")[-1] }.each do |group, t2|
    no_schema_tables[group] = t2.sort_by { |t| search_path_index[t.split(".")[0]] || 1000000 }[0]
  end

  # filter queries from other databases and system tables
  queries.each do |query|
    # add schema to table if needed
    query.tables = query.tables.map { |t| no_schema_tables[t] || t }

    # check for missing tables
    query.missing_tables = !query.tables.all? { |t| tables.include?(t) }
  end

  # set tables
  tables = Set.new(queries.reject(&:missing_tables).flat_map(&:tables))

  # analyze tables if needed
  analyze_tables(tables) if tables.any? && (@analyze || @log_level == "debug2")

  # create hypothetical indexes and explain queries
  candidates = tables.any? ? create_hypothetical_indexes(queries.reject(&:missing_tables), tables) : {}

  # see if new indexes were used and meet bar
  new_indexes = determine_indexes(queries, candidates, tables)

  # display and create new indexes
  show_and_create_indexes(new_indexes, queries, tables)
end

#process_stat_statementsObject



21
22
23
24
25
# File 'lib/dexter/indexer.rb', line 21

def process_stat_statements
  queries = stat_statements.map { |q| Query.new(q) }.sort_by(&:fingerprint).group_by(&:fingerprint).map { |_, v| v.first }
  log "Processing #{queries.size} new query fingerprints"
  process_queries(queries)
end