row_count.rb 2.46 KB
Newer Older
1
2
3
4
5
module GitLab
  module Monitor
    module Database
      # A helper class that executes the query its given and returns an int of
      # the row count
6
7
8
      # This class works under the assumption you do COUNT(*) queries, define
      # queries in the QUERIES constant. If in doubt how these work, read
      # #construct_query
9
      class RowCountCollector < Base
10
        QUERIES = {
11
          groups: { select: :namespaces, where: "type='Group'" },
12
          projects: { select: :projects, where: "pending_delete=false" },
13
14
15
16
17
18
          soft_deleted_projects: { select: :projects, where: "pending_delete=true" },
          orphaned_projects: {
            select: :projects,
            joins: "LEFT JOIN namespaces ON projects.namespace_id = namespaces.id",
            where: "namespaces.id IS NULL"
          },
19
20
          personal_snippets: { select: :snippets, where: "type='PersonalSnippet'" },
          project_snippets: { select: :snippets, where: "type='ProjectSnippet'" },
Z.J. van de Weg's avatar
Z.J. van de Weg committed
21
22
          users: { select: :users },
          active_runners: { select: :ci_runners, where: "active=true AND contacted_at > current_date - 1" }
23
        }.freeze
24
25
26
27

        def run
          results = Hash.new(0)

28
29
          QUERIES.each do |key, query|
            results[key] = execute(query)
30
31
32
33
34
35
36
37
          end

          results
        end

        private

        def execute(query)
38
          connection.exec(construct_query(query))[0]["count"]
39
40
        rescue PG::UndefinedTable, PG::UndefinedColumn
          0
41
42
        end

43
44
45
46
47
48
        # Not private so I can test it without meta programming tricks
        def construct_query(query)
          query_string = "SELECT COUNT(*) FROM #{query[:select]} "
          query_string << "#{query[:joins]} "       if query[:joins]
          query_string << "WHERE #{query[:where]}"  if query[:where]
          query_string << ";"
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
        end
      end

      # The prober which is called when gathering metrics
      class RowCountProber
        def initialize(opts, metrics: PrometheusMetrics.new)
          @metrics = metrics
          @collector = RowCountCollector.new(connection_string: opts[:connection_string])
        end

        def probe_db
          return self unless @collector.connected?

          results = @collector.run
          results.each do |key, value|
64
            @metrics.add("db_rows_count", value.to_i, query_name: key.to_s)
65
66
67
68
69
70
71
72
73
74
75
76
          end

          self
        end

        def write_to(target)
          target.write(@metrics.to_s)
        end
      end
    end
  end
end