gitlab-exporter 16.8.0 → 16.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile.lock +5 -5
- data/gitlab-exporter.gemspec +2 -2
- data/lib/gitlab_exporter/database/bloat_btree.sql +143 -97
- data/lib/gitlab_exporter/database/bloat_table.sql +10 -3
- data/lib/gitlab_exporter/database/zoekt.rb +104 -26
- data/lib/gitlab_exporter/version.rb +1 -1
- data/spec/database/zoekt_spec.rb +118 -1
- metadata +5 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3a3b7fa869eab272c352626dfd0f8acc2cbcc3fb2df46da92ea0387471282d2b
|
|
4
|
+
data.tar.gz: fc2f8a0a213b2a97a92ed5f417a5856f181c9f422cbbc4640c9cce45d86e4fcf
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2f6603c161ae6c9346d5b3ecd919f4df54bceb8232e588db181d1142eedc2e90231039b15a357ceed1ae62ae735b7540567e2717bbacadcea5e83ddd5b366fb2
|
|
7
|
+
data.tar.gz: 760580b43d2290a933842a4ffbd4182fe60660b401d03e9df6a7c8d85f6c3363054124b9aba2fd5a4983fb1989a11f2df28cf2f3cbdf7ca67e653bde4be95bf7
|
data/Gemfile.lock
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
gitlab-exporter (16.
|
|
4
|
+
gitlab-exporter (16.10.0)
|
|
5
5
|
connection_pool (= 2.5.5)
|
|
6
6
|
deep_merge (~> 1.2.2)
|
|
7
|
-
faraday (= 2.14.
|
|
7
|
+
faraday (= 2.14.3)
|
|
8
8
|
pg (= 1.6.3)
|
|
9
|
-
puma (= 8.0.
|
|
9
|
+
puma (= 8.0.2)
|
|
10
10
|
quantile (= 0.2.1)
|
|
11
11
|
redis (= 4.8.1)
|
|
12
12
|
redis-namespace (= 1.11.0)
|
|
@@ -22,7 +22,7 @@ GEM
|
|
|
22
22
|
connection_pool (2.5.5)
|
|
23
23
|
deep_merge (1.2.2)
|
|
24
24
|
diff-lcs (1.5.0)
|
|
25
|
-
faraday (2.14.
|
|
25
|
+
faraday (2.14.3)
|
|
26
26
|
faraday-net_http (>= 2.0, < 3.5)
|
|
27
27
|
json
|
|
28
28
|
logger
|
|
@@ -40,7 +40,7 @@ GEM
|
|
|
40
40
|
racc
|
|
41
41
|
pg (1.6.3)
|
|
42
42
|
prism (1.9.0)
|
|
43
|
-
puma (8.0.
|
|
43
|
+
puma (8.0.2)
|
|
44
44
|
nio4r (~> 2.0)
|
|
45
45
|
quantile (0.2.1)
|
|
46
46
|
racc (1.8.1)
|
data/gitlab-exporter.gemspec
CHANGED
|
@@ -24,9 +24,9 @@ Gem::Specification.new do |s|
|
|
|
24
24
|
|
|
25
25
|
s.add_runtime_dependency "connection_pool", "2.5.5"
|
|
26
26
|
s.add_runtime_dependency "deep_merge", "~> 1.2.2"
|
|
27
|
-
s.add_runtime_dependency "faraday", "2.14.
|
|
27
|
+
s.add_runtime_dependency "faraday", "2.14.3"
|
|
28
28
|
s.add_runtime_dependency "pg", "1.6.3"
|
|
29
|
-
s.add_runtime_dependency "puma", "8.0.
|
|
29
|
+
s.add_runtime_dependency "puma", "8.0.2"
|
|
30
30
|
s.add_runtime_dependency "quantile", "0.2.1"
|
|
31
31
|
s.add_runtime_dependency "redis", "4.8.1"
|
|
32
32
|
s.add_runtime_dependency "redis-namespace", "1.11.0"
|
|
@@ -1,101 +1,147 @@
|
|
|
1
1
|
-- Originally from: https://github.com/ioguix/pgsql-bloat-estimation/blob/master/btree/btree_bloat.sql
|
|
2
|
-
--
|
|
2
|
+
--
|
|
3
|
+
-- Rewritten to use CROSS JOIN LATERAL stages so this query stays in sync with the
|
|
4
|
+
-- `postgres_index_bloat_estimate(p_schema, p_idxname)` function used by the main GitLab
|
|
5
|
+
-- application's reindexing pipeline:
|
|
6
|
+
-- https://gitlab.com/gitlab-org/gitlab/-/blob/master/db/migrate/20260526120000_add_postgres_index_bloat_estimate_function.rb
|
|
7
|
+
--
|
|
8
|
+
-- Expanding the index columns inside a LATERAL join (instead of putting a set-returning
|
|
9
|
+
-- function such as generate_series in the SELECT list) avoids the ProjectSet barrier that
|
|
10
|
+
-- prevents the planner from pushing predicates down. See:
|
|
11
|
+
-- https://gitlab.com/gitlab-org/gitlab/-/work_items/569363
|
|
12
|
+
-- https://gitlab.com/gitlab-org/gitlab/-/work_items/599210
|
|
13
|
+
--
|
|
14
|
+
-- WARNING: executed with a non-superuser role, the query inspects only indexes on tables you are granted to read.
|
|
3
15
|
-- WARNING: rows with is_na = 't' are known to have bad statistics ("name" type is not supported).
|
|
4
|
-
-- This query is compatible with PostgreSQL
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
16
|
+
-- This query is compatible with PostgreSQL 9.3 and after (LATERAL joins).
|
|
17
|
+
--
|
|
18
|
+
-- Note: unlike the previous ioguix-style query, this form does NOT need to materialize
|
|
19
|
+
-- pg_stats to stay fast on PostgreSQL 17. The old query joined pg_stats once as a single
|
|
20
|
+
-- large join that the PG17 planner badly underestimated (see f18ac4b); here pg_stats is
|
|
21
|
+
-- looked up per index inside the LATERAL, resolving through the pg_statistic indexes, so
|
|
22
|
+
-- there is no single join for the planner to misestimate.
|
|
23
|
+
SELECT
|
|
24
|
+
current_database(),
|
|
25
|
+
n.nspname AS schemaname,
|
|
26
|
+
ct.relname AS tblname,
|
|
27
|
+
ci.relname AS object_name,
|
|
28
|
+
c.bs * ci.relpages::bigint AS real_size,
|
|
29
|
+
c.bs * (ci.relpages - bloat.est_pages)::bigint AS extra_size,
|
|
30
|
+
100 * (ci.relpages - bloat.est_pages)::float / ci.relpages AS extra_ratio,
|
|
31
|
+
c.fillfactor,
|
|
32
|
+
CASE WHEN ci.relpages > bloat.est_pages_ff
|
|
33
|
+
THEN c.bs * (ci.relpages - bloat.est_pages_ff)
|
|
11
34
|
ELSE 0
|
|
12
35
|
END AS bloat_size,
|
|
13
|
-
100 * (relpages-est_pages_ff)::float / relpages AS bloat_ratio,
|
|
14
|
-
is_na
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
36
|
+
100 * (ci.relpages - bloat.est_pages_ff)::float / ci.relpages AS bloat_ratio,
|
|
37
|
+
bloat.is_na
|
|
38
|
+
FROM pg_catalog.pg_class ci
|
|
39
|
+
JOIN pg_catalog.pg_index i ON i.indexrelid = ci.oid
|
|
40
|
+
JOIN pg_catalog.pg_class ct ON ct.oid = i.indrelid
|
|
41
|
+
JOIN pg_catalog.pg_namespace n ON n.oid = ct.relnamespace
|
|
42
|
+
|
|
43
|
+
-- Per-index system constants and fillfactor.
|
|
44
|
+
CROSS JOIN LATERAL (
|
|
45
|
+
SELECT
|
|
46
|
+
current_setting('block_size')::numeric AS bs,
|
|
47
|
+
CASE -- MAXALIGN: 4 on 32bits, 8 on 64bits (and mingw32 ?)
|
|
48
|
+
WHEN version() ~ 'mingw32'::text
|
|
49
|
+
OR version() ~ '64-bit|x86_64|ppc64|ia64|amd64'::text THEN 8
|
|
50
|
+
ELSE 4
|
|
51
|
+
END AS maxalign,
|
|
52
|
+
24 AS pagehdr, -- per page header, fixed size: 24 for 8.3 and after
|
|
53
|
+
16 AS pageopqdata, -- per page btree opaque data
|
|
54
|
+
COALESCE(
|
|
55
|
+
substring(array_to_string(ci.reloptions, ' '::text) FROM 'fillfactor=([0-9]+)')::smallint::integer,
|
|
56
|
+
90
|
|
57
|
+
) AS fillfactor
|
|
58
|
+
) c
|
|
59
|
+
|
|
60
|
+
-- Aggregate the per-column statistics for this index, expanding the index columns
|
|
61
|
+
-- through a LATERAL generate_series rather than a SELECT-list set-returning function.
|
|
62
|
+
CROSS JOIN LATERAL (
|
|
63
|
+
SELECT
|
|
64
|
+
max(CASE WHEN COALESCE(a1.atttypid, a2.atttypid) = 'pg_catalog.name'::regtype THEN 1 ELSE 0 END) > 0 AS is_na,
|
|
65
|
+
/* per tuple header: add IndexAttributeBitMapData if some cols are null-able */
|
|
66
|
+
CASE WHEN max(COALESCE(s_t.null_frac, s_x.null_frac, 0::real)) = 0::double precision
|
|
67
|
+
THEN 2 -- IndexTupleData size
|
|
68
|
+
ELSE 2 + (32 + 8 - 1) / 8 -- IndexTupleData size + IndexAttributeBitMapData size
|
|
69
|
+
END AS index_tuple_hdr_bm,
|
|
70
|
+
/* data len: we remove null values, saving space, using their fractional part from stats */
|
|
71
|
+
sum((1::double precision - COALESCE(s_t.null_frac, s_x.null_frac, 0::real))
|
|
72
|
+
* COALESCE(s_t.avg_width, s_x.avg_width, 1024)::double precision) AS nulldatawidth
|
|
73
|
+
FROM generate_series(1, i.indnatts::integer) AS gs(attpos)
|
|
74
|
+
-- Decode the indkey int2vector into an int[] once per index (not once per column).
|
|
75
|
+
CROSS JOIN LATERAL (
|
|
76
|
+
SELECT string_to_array(textin(int2vectorout(i.indkey)), ' '::text)::integer[] AS indkey_arr
|
|
77
|
+
) ik
|
|
78
|
+
LEFT JOIN pg_catalog.pg_attribute a1
|
|
79
|
+
ON ik.indkey_arr[gs.attpos] <> 0
|
|
80
|
+
AND a1.attrelid = i.indrelid
|
|
81
|
+
AND a1.attnum = ik.indkey_arr[gs.attpos]
|
|
82
|
+
LEFT JOIN pg_catalog.pg_attribute a2
|
|
83
|
+
ON ik.indkey_arr[gs.attpos] = 0
|
|
84
|
+
AND a2.attrelid = ci.oid
|
|
85
|
+
AND a2.attnum = gs.attpos
|
|
86
|
+
LEFT JOIN pg_catalog.pg_stats s_t
|
|
87
|
+
ON s_t.schemaname = n.nspname
|
|
88
|
+
AND s_t.tablename = ct.relname
|
|
89
|
+
AND s_t.attname = a1.attname
|
|
90
|
+
LEFT JOIN pg_catalog.pg_stats s_x
|
|
91
|
+
ON s_x.schemaname = n.nspname
|
|
92
|
+
AND s_x.tablename = ci.relname
|
|
93
|
+
AND s_x.attname = a2.attname
|
|
94
|
+
) agg
|
|
95
|
+
|
|
96
|
+
-- Index tuple header width, aligned on MAXALIGN.
|
|
97
|
+
CROSS JOIN LATERAL (
|
|
98
|
+
SELECT
|
|
99
|
+
(
|
|
100
|
+
(agg.index_tuple_hdr_bm + c.maxalign
|
|
101
|
+
- CASE -- Add padding to the index tuple header to align on MAXALIGN
|
|
102
|
+
WHEN (agg.index_tuple_hdr_bm % c.maxalign) = 0 THEN c.maxalign
|
|
103
|
+
ELSE agg.index_tuple_hdr_bm % c.maxalign
|
|
104
|
+
END)::double precision
|
|
105
|
+
+ agg.nulldatawidth
|
|
106
|
+
+ c.maxalign::double precision
|
|
107
|
+
- CASE -- Add padding to the data to align on MAXALIGN
|
|
108
|
+
WHEN agg.nulldatawidth = 0::double precision THEN 0
|
|
109
|
+
WHEN (agg.nulldatawidth::integer % c.maxalign) = 0 THEN c.maxalign
|
|
110
|
+
ELSE agg.nulldatawidth::integer % c.maxalign
|
|
111
|
+
END::double precision
|
|
112
|
+
)::numeric AS nulldatahdrwidth
|
|
113
|
+
) hdr
|
|
114
|
+
|
|
115
|
+
-- Estimated number of pages, with and without the fillfactor applied.
|
|
116
|
+
CROSS JOIN LATERAL (
|
|
117
|
+
SELECT
|
|
118
|
+
agg.is_na,
|
|
119
|
+
-- NULLIF guards against division by zero: floor(...) reaches 0 once
|
|
120
|
+
-- nulldatahdrwidth exceeds the usable page size (an index whose columns are
|
|
121
|
+
-- entirely unanalyzed and fall back to the 1024-byte default width). Without
|
|
122
|
+
-- it, PostgreSQL raises a hard error that would fail the whole probe; instead
|
|
123
|
+
-- the NULL flows through the outer COALESCE to est_pages = 0.
|
|
124
|
+
COALESCE(
|
|
125
|
+
1::double precision + ceil(
|
|
126
|
+
ci.reltuples / NULLIF(floor(
|
|
127
|
+
(c.bs - c.pageopqdata::numeric - c.pagehdr::numeric)::double precision
|
|
128
|
+
/ (4::numeric + hdr.nulldatahdrwidth)::double precision
|
|
129
|
+
), 0)
|
|
130
|
+
),
|
|
131
|
+
0::double precision
|
|
132
|
+
) AS est_pages,
|
|
133
|
+
COALESCE(
|
|
134
|
+
1::double precision + ceil(
|
|
135
|
+
ci.reltuples / NULLIF(floor(
|
|
136
|
+
((c.bs - c.pageopqdata::numeric - c.pagehdr::numeric) * c.fillfactor::numeric)::double precision
|
|
137
|
+
/ (100::double precision * (4::numeric + hdr.nulldatahdrwidth)::double precision)
|
|
138
|
+
), 0)
|
|
139
|
+
),
|
|
140
|
+
0::double precision
|
|
141
|
+
) AS est_pages_ff
|
|
142
|
+
) bloat
|
|
143
|
+
|
|
144
|
+
WHERE ci.relkind = 'i'
|
|
145
|
+
AND ci.relam = (SELECT oid FROM pg_am WHERE amname = 'btree'::name)
|
|
146
|
+
AND ci.relpages > 0
|
|
147
|
+
ORDER BY n.nspname, ct.relname, ci.relname;
|
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
-- Originally from: https://github.com/ioguix/pgsql-bloat-estimation/blob/master/table/table_bloat.sql
|
|
2
2
|
/* WARNING: executed with a non-superuser role, the query inspect only tables and materialized view (9.3+) you are granted to read.
|
|
3
|
-
* This query is compatible with PostgreSQL
|
|
3
|
+
* This query is compatible with PostgreSQL 12 and later
|
|
4
4
|
*/
|
|
5
|
+
-- pg_stats is materialized so the planner sees the true row count before planning the join,
|
|
6
|
+
-- avoiding a catastrophic cardinality underestimate on PostgreSQL 17.
|
|
7
|
+
WITH pg_stats_cached AS MATERIALIZED (
|
|
8
|
+
SELECT schemaname, tablename, attname, null_frac, avg_width
|
|
9
|
+
FROM pg_stats
|
|
10
|
+
WHERE inherited = false
|
|
11
|
+
)
|
|
5
12
|
SELECT current_database(), schemaname, tblname AS object_name, bs*tblpages AS real_size,
|
|
6
13
|
(tblpages-est_tblpages)*bs AS extra_size,
|
|
7
14
|
CASE WHEN tblpages - est_tblpages > 0
|
|
@@ -49,8 +56,8 @@ FROM (
|
|
|
49
56
|
FROM pg_attribute AS att
|
|
50
57
|
JOIN pg_class AS tbl ON att.attrelid = tbl.oid
|
|
51
58
|
JOIN pg_namespace AS ns ON ns.oid = tbl.relnamespace
|
|
52
|
-
LEFT JOIN
|
|
53
|
-
AND s.tablename = tbl.relname AND s.
|
|
59
|
+
LEFT JOIN pg_stats_cached AS s ON s.schemaname=ns.nspname
|
|
60
|
+
AND s.tablename = tbl.relname AND s.attname=att.attname
|
|
54
61
|
LEFT JOIN pg_class AS toast ON tbl.reltoastrelid = toast.oid
|
|
55
62
|
WHERE NOT att.attisdropped
|
|
56
63
|
AND tbl.relkind in ('r','m')
|
|
@@ -2,6 +2,7 @@ module GitLab
|
|
|
2
2
|
module Exporter
|
|
3
3
|
module Database
|
|
4
4
|
# A helper class to collect zoekt metrics.
|
|
5
|
+
# rubocop:disable Metrics/ClassLength
|
|
5
6
|
class ZoektCollector < Base
|
|
6
7
|
# Query to get processing zoekt_tasks distribution by zoekt_node_id
|
|
7
8
|
ZOEKT_TASKS_PROCESSING_QUERY = <<~SQL.freeze
|
|
@@ -108,6 +109,40 @@ module GitLab
|
|
|
108
109
|
zoekt_nodes ON zoekt_indices.zoekt_node_id = zoekt_nodes.id
|
|
109
110
|
SQL
|
|
110
111
|
|
|
112
|
+
ZOEKT_NODE_ENABLED_NAMESPACES_QUERY = <<~SQL.freeze
|
|
113
|
+
SELECT
|
|
114
|
+
zn.id AS node_id,
|
|
115
|
+
zn.metadata ->> 'name' AS node_name,
|
|
116
|
+
COUNT(DISTINCT zi.zoekt_enabled_namespace_id) AS count
|
|
117
|
+
FROM zoekt_nodes zn
|
|
118
|
+
LEFT JOIN zoekt_indices zi ON zi.zoekt_node_id = zn.id
|
|
119
|
+
GROUP BY zn.id, zn.metadata ->> 'name'
|
|
120
|
+
SQL
|
|
121
|
+
|
|
122
|
+
ZOEKT_NODE_TASKS_QUERY = <<~SQL.freeze
|
|
123
|
+
SELECT
|
|
124
|
+
zn.id AS node_id,
|
|
125
|
+
zn.metadata ->> 'name' AS node_name,
|
|
126
|
+
zt.state,
|
|
127
|
+
COUNT(*) AS count
|
|
128
|
+
FROM zoekt_nodes zn
|
|
129
|
+
LEFT JOIN zoekt_tasks zt ON zt.zoekt_node_id = zn.id
|
|
130
|
+
GROUP BY zn.id, zn.metadata ->> 'name', zt.state
|
|
131
|
+
SQL
|
|
132
|
+
|
|
133
|
+
ZOEKT_INDICES_STALE_USED_STORAGE_QUERY = <<~SQL.freeze
|
|
134
|
+
SELECT COUNT(*) AS count FROM zoekt_indices WHERE last_indexed_at >= used_storage_bytes_updated_at
|
|
135
|
+
SQL
|
|
136
|
+
|
|
137
|
+
ZOEKT_TASK_STATES = {
|
|
138
|
+
0 => "pending",
|
|
139
|
+
1 => "processing",
|
|
140
|
+
10 => "done",
|
|
141
|
+
250 => "skipped",
|
|
142
|
+
255 => "failed",
|
|
143
|
+
256 => "orphaned"
|
|
144
|
+
}.freeze
|
|
145
|
+
|
|
111
146
|
def run
|
|
112
147
|
return {} unless zoekt_indexing_enabled?
|
|
113
148
|
|
|
@@ -119,7 +154,10 @@ module GitLab
|
|
|
119
154
|
repositories_state_query_result: execute(ZOEKT_REPOSITORIES_STATE_QUERY),
|
|
120
155
|
indices_state_query_result: execute(ZOEKT_INDICES_STATE_QUERY),
|
|
121
156
|
indices_watermark_query_result: execute(ZOEKT_INDICES_WATERMARK_QUERY),
|
|
122
|
-
indices_storage_query_result: execute(ZOEKT_INDICES_STORAGE_QUERY)
|
|
157
|
+
indices_storage_query_result: execute(ZOEKT_INDICES_STORAGE_QUERY),
|
|
158
|
+
node_enabled_namespaces_query_result: execute(ZOEKT_NODE_ENABLED_NAMESPACES_QUERY),
|
|
159
|
+
node_tasks_query_result: execute(ZOEKT_NODE_TASKS_QUERY),
|
|
160
|
+
indices_stale_used_storage_query_result: execute(ZOEKT_INDICES_STALE_USED_STORAGE_QUERY)
|
|
123
161
|
}.compact
|
|
124
162
|
end
|
|
125
163
|
|
|
@@ -181,6 +219,7 @@ module GitLab
|
|
|
181
219
|
nil
|
|
182
220
|
end
|
|
183
221
|
end
|
|
222
|
+
# rubocop:enable Metrics/ClassLength
|
|
184
223
|
|
|
185
224
|
# The prober which is called when gathering metrics
|
|
186
225
|
# rubocop:disable Metrics/ClassLength
|
|
@@ -245,6 +284,24 @@ module GitLab
|
|
|
245
284
|
"gauge"
|
|
246
285
|
)
|
|
247
286
|
|
|
287
|
+
PrometheusMetrics.describe(
|
|
288
|
+
"search_zoekt_node_enabled_namespaces",
|
|
289
|
+
"Number of enabled namespaces per Zoekt node",
|
|
290
|
+
"gauge"
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
PrometheusMetrics.describe(
|
|
294
|
+
"search_zoekt_node_tasks",
|
|
295
|
+
"Number of Zoekt indexing tasks on a node, broken down by state",
|
|
296
|
+
"gauge"
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
PrometheusMetrics.describe(
|
|
300
|
+
"search_zoekt_indices_with_stale_used_storage_bytes",
|
|
301
|
+
"Number of Zoekt indices whose used_storage_bytes value has not been updated since the last index run",
|
|
302
|
+
"gauge"
|
|
303
|
+
)
|
|
304
|
+
|
|
248
305
|
def initialize(metrics: PrometheusMetrics.new, **opts)
|
|
249
306
|
@metrics = metrics
|
|
250
307
|
@collector = opts[:collector] || ZoektCollector.new(**opts)
|
|
@@ -269,6 +326,9 @@ module GitLab
|
|
|
269
326
|
process_indices_state_results(results[:indices_state_query_result])
|
|
270
327
|
process_indices_watermark_results(results[:indices_watermark_query_result])
|
|
271
328
|
process_indices_storage_results(results[:indices_storage_query_result])
|
|
329
|
+
process_node_enabled_namespaces_results(results[:node_enabled_namespaces_query_result])
|
|
330
|
+
process_node_tasks_results(results[:node_tasks_query_result])
|
|
331
|
+
process_indices_stale_used_storage_results(results[:indices_stale_used_storage_query_result])
|
|
272
332
|
end
|
|
273
333
|
|
|
274
334
|
def process_task_results(results)
|
|
@@ -363,23 +423,11 @@ module GitLab
|
|
|
363
423
|
end
|
|
364
424
|
|
|
365
425
|
def add_zoekt_repositories_state_to_metric(row)
|
|
366
|
-
@metrics.add(
|
|
367
|
-
"search_zoekt_repositories_states_total",
|
|
368
|
-
row["count"].to_i,
|
|
369
|
-
**{
|
|
370
|
-
state: row["state"]
|
|
371
|
-
}.compact
|
|
372
|
-
)
|
|
426
|
+
@metrics.add("search_zoekt_repositories_states_total", row["count"].to_i, **{ state: row["state"] }.compact)
|
|
373
427
|
end
|
|
374
428
|
|
|
375
429
|
def add_zoekt_indices_state_to_metric(row)
|
|
376
|
-
@metrics.add(
|
|
377
|
-
"search_zoekt_indices_states",
|
|
378
|
-
row["count"].to_i,
|
|
379
|
-
**{
|
|
380
|
-
state: row["state"]
|
|
381
|
-
}.compact
|
|
382
|
-
)
|
|
430
|
+
@metrics.add("search_zoekt_indices_states", row["count"].to_i, **{ state: row["state"] }.compact)
|
|
383
431
|
end
|
|
384
432
|
|
|
385
433
|
def add_zoekt_indices_watermark_to_metric(row)
|
|
@@ -392,26 +440,56 @@ module GitLab
|
|
|
392
440
|
)
|
|
393
441
|
end
|
|
394
442
|
|
|
395
|
-
|
|
396
|
-
|
|
443
|
+
def process_node_enabled_namespaces_results(results)
|
|
444
|
+
results&.each { |row| add_zoekt_node_enabled_namespaces_to_metric(row) }
|
|
445
|
+
end
|
|
446
|
+
|
|
447
|
+
def process_node_tasks_results(results)
|
|
448
|
+
results&.each { |row| add_zoekt_node_tasks_to_metric(row) }
|
|
449
|
+
end
|
|
450
|
+
|
|
451
|
+
def process_indices_stale_used_storage_results(results)
|
|
452
|
+
results&.each { |row| add_zoekt_indices_stale_used_storage_to_metric(row) }
|
|
453
|
+
end
|
|
454
|
+
|
|
455
|
+
def add_zoekt_node_enabled_namespaces_to_metric(row)
|
|
397
456
|
@metrics.add(
|
|
398
|
-
"
|
|
399
|
-
row["
|
|
457
|
+
"search_zoekt_node_enabled_namespaces",
|
|
458
|
+
row["count"].to_i,
|
|
400
459
|
**{
|
|
401
|
-
|
|
402
|
-
|
|
460
|
+
node_id: row["node_id"],
|
|
461
|
+
node_name: row["node_name"]
|
|
403
462
|
}.compact
|
|
404
463
|
)
|
|
464
|
+
end
|
|
465
|
+
|
|
466
|
+
def add_zoekt_node_tasks_to_metric(row)
|
|
467
|
+
state_int = row["state"]&.to_i
|
|
468
|
+
return if row["state"].nil?
|
|
469
|
+
|
|
470
|
+
state_label = ZoektCollector::ZOEKT_TASK_STATES[state_int]
|
|
471
|
+
return if state_label.nil?
|
|
472
|
+
|
|
405
473
|
@metrics.add(
|
|
406
|
-
"
|
|
407
|
-
row["
|
|
474
|
+
"search_zoekt_node_tasks",
|
|
475
|
+
row["count"].to_i,
|
|
408
476
|
**{
|
|
409
|
-
|
|
410
|
-
|
|
477
|
+
node_id: row["node_id"],
|
|
478
|
+
node_name: row["node_name"],
|
|
479
|
+
state: state_label
|
|
411
480
|
}.compact
|
|
412
481
|
)
|
|
413
482
|
end
|
|
414
|
-
|
|
483
|
+
|
|
484
|
+
def add_zoekt_indices_stale_used_storage_to_metric(row)
|
|
485
|
+
@metrics.add("search_zoekt_indices_with_stale_used_storage_bytes", row["count"].to_i)
|
|
486
|
+
end
|
|
487
|
+
|
|
488
|
+
def add_zoekt_indices_storage_to_metric(row)
|
|
489
|
+
payload = { zoekt_index_id: row["id"], zoekt_node_name: row["node_name"] }.compact
|
|
490
|
+
@metrics.add("search_zoekt_indices_reserved_storage_bytes", row["reserved_storage_bytes"].to_i, **payload)
|
|
491
|
+
@metrics.add("search_zoekt_indices_used_storage_bytes", row["used_storage_bytes"].to_i, **payload)
|
|
492
|
+
end
|
|
415
493
|
|
|
416
494
|
def write_to(target)
|
|
417
495
|
target.write(@metrics.to_s)
|
data/spec/database/zoekt_spec.rb
CHANGED
|
@@ -36,6 +36,23 @@ describe GitLab::Exporter::Database::ZoektCollector do
|
|
|
36
36
|
"node_name" => "zoekt-2" }
|
|
37
37
|
]
|
|
38
38
|
end
|
|
39
|
+
let(:zoekt_node_enabled_namespaces_query) { described_class::ZOEKT_NODE_ENABLED_NAMESPACES_QUERY }
|
|
40
|
+
let(:zoekt_node_enabled_namespaces_query_results) do
|
|
41
|
+
[
|
|
42
|
+
{ "node_id" => "1", "node_name" => "zoekt-1", "count" => "3" },
|
|
43
|
+
{ "node_id" => "2", "node_name" => "zoekt-2", "count" => "5" }
|
|
44
|
+
]
|
|
45
|
+
end
|
|
46
|
+
let(:zoekt_node_tasks_query) { described_class::ZOEKT_NODE_TASKS_QUERY }
|
|
47
|
+
let(:zoekt_node_tasks_query_results) do
|
|
48
|
+
[
|
|
49
|
+
{ "node_id" => "1", "node_name" => "zoekt-1", "state" => "0", "count" => "4" },
|
|
50
|
+
{ "node_id" => "1", "node_name" => "zoekt-1", "state" => "1", "count" => "2" },
|
|
51
|
+
{ "node_id" => "2", "node_name" => "zoekt-2", "state" => "10", "count" => "7" }
|
|
52
|
+
]
|
|
53
|
+
end
|
|
54
|
+
let(:zoekt_indices_stale_used_storage_query) { described_class::ZOEKT_INDICES_STALE_USED_STORAGE_QUERY }
|
|
55
|
+
let(:zoekt_indices_stale_used_storage_query_results) { [{ "count" => "6" }] }
|
|
39
56
|
|
|
40
57
|
let(:zoekt_repository_schema_version_query_results) { [{ "count" => "1" }] }
|
|
41
58
|
|
|
@@ -80,7 +97,10 @@ describe GitLab::Exporter::Database::ZoektCollector do
|
|
|
80
97
|
repositories_state_query_result: zoekt_repositories_state_query_results,
|
|
81
98
|
indices_state_query_result: zoekt_indices_state_query_results,
|
|
82
99
|
indices_watermark_query_result: zoekt_indices_watermark_query_results,
|
|
83
|
-
indices_storage_query_result: zoekt_indices_storage_query_results
|
|
100
|
+
indices_storage_query_result: zoekt_indices_storage_query_results,
|
|
101
|
+
node_enabled_namespaces_query_result: zoekt_node_enabled_namespaces_query_results,
|
|
102
|
+
node_tasks_query_result: zoekt_node_tasks_query_results,
|
|
103
|
+
indices_stale_used_storage_query_result: zoekt_indices_stale_used_storage_query_results
|
|
84
104
|
}
|
|
85
105
|
end
|
|
86
106
|
|
|
@@ -122,6 +142,12 @@ describe GitLab::Exporter::Database::ZoektCollector do
|
|
|
122
142
|
.and_return(zoekt_indices_watermark_query_results)
|
|
123
143
|
expect(connection).to receive(:exec_params).with(zoekt_indices_storage_query, [])
|
|
124
144
|
.and_return(zoekt_indices_storage_query_results)
|
|
145
|
+
expect(connection).to receive(:exec_params).with(zoekt_node_enabled_namespaces_query, [])
|
|
146
|
+
.and_return(zoekt_node_enabled_namespaces_query_results)
|
|
147
|
+
expect(connection).to receive(:exec_params).with(zoekt_node_tasks_query, [])
|
|
148
|
+
.and_return(zoekt_node_tasks_query_results)
|
|
149
|
+
expect(connection).to receive(:exec_params).with(zoekt_indices_stale_used_storage_query, [])
|
|
150
|
+
.and_return(zoekt_indices_stale_used_storage_query_results)
|
|
125
151
|
|
|
126
152
|
expect(collector.run).to eq(result)
|
|
127
153
|
end
|
|
@@ -144,6 +170,12 @@ describe GitLab::Exporter::Database::ZoektCollector do
|
|
|
144
170
|
.and_raise(PG::UndefinedTable)
|
|
145
171
|
allow(connection).to receive(:exec_params).with(zoekt_indices_storage_query, [])
|
|
146
172
|
.and_raise(PG::UndefinedTable)
|
|
173
|
+
allow(connection).to receive(:exec_params).with(zoekt_node_enabled_namespaces_query, [])
|
|
174
|
+
.and_raise(PG::UndefinedTable)
|
|
175
|
+
allow(connection).to receive(:exec_params).with(zoekt_node_tasks_query, [])
|
|
176
|
+
.and_raise(PG::UndefinedTable)
|
|
177
|
+
allow(connection).to receive(:exec_params).with(zoekt_indices_stale_used_storage_query, [])
|
|
178
|
+
.and_raise(PG::UndefinedTable)
|
|
147
179
|
|
|
148
180
|
expect(collector.run).to eq({})
|
|
149
181
|
end
|
|
@@ -167,6 +199,12 @@ describe GitLab::Exporter::Database::ZoektCollector do
|
|
|
167
199
|
.and_raise(PG::UndefinedColumn)
|
|
168
200
|
expect(connection).to receive(:exec_params).with(zoekt_indices_storage_query, [])
|
|
169
201
|
.and_raise(PG::UndefinedColumn)
|
|
202
|
+
expect(connection).to receive(:exec_params).with(zoekt_node_enabled_namespaces_query, [])
|
|
203
|
+
.and_raise(PG::UndefinedColumn)
|
|
204
|
+
expect(connection).to receive(:exec_params).with(zoekt_node_tasks_query, [])
|
|
205
|
+
.and_raise(PG::UndefinedColumn)
|
|
206
|
+
expect(connection).to receive(:exec_params).with(zoekt_indices_stale_used_storage_query, [])
|
|
207
|
+
.and_raise(PG::UndefinedColumn)
|
|
170
208
|
|
|
171
209
|
expect(collector.run).to eq({ task_processing_query_result: zoekt_tasks_processing_query_results })
|
|
172
210
|
end
|
|
@@ -257,6 +295,19 @@ describe GitLab::Exporter::Database::ZoektProber do
|
|
|
257
295
|
"node_name" => "zoekt-1" },
|
|
258
296
|
{ "id" => "2", "reserved_storage_bytes" => "2000000", "used_storage_bytes" => "1500000",
|
|
259
297
|
"node_name" => "zoekt-2" }
|
|
298
|
+
],
|
|
299
|
+
node_enabled_namespaces_query_result: [
|
|
300
|
+
{ "node_id" => "1", "node_name" => "zoekt-1", "count" => "3" },
|
|
301
|
+
{ "node_id" => "2", "node_name" => "zoekt-2", "count" => "5" }
|
|
302
|
+
],
|
|
303
|
+
node_tasks_query_result: [
|
|
304
|
+
{ "node_id" => "1", "node_name" => "zoekt-1", "state" => "0", "count" => "4" },
|
|
305
|
+
{ "node_id" => "1", "node_name" => "zoekt-1", "state" => "1", "count" => "2" },
|
|
306
|
+
{ "node_id" => "2", "node_name" => "zoekt-2", "state" => "10", "count" => "7" },
|
|
307
|
+
{ "node_id" => "2", "node_name" => "zoekt-2", "state" => nil, "count" => "1" }
|
|
308
|
+
],
|
|
309
|
+
indices_stale_used_storage_query_result: [
|
|
310
|
+
{ "count" => "6" }
|
|
260
311
|
]
|
|
261
312
|
}
|
|
262
313
|
end
|
|
@@ -361,7 +412,73 @@ describe GitLab::Exporter::Database::ZoektProber do
|
|
|
361
412
|
)
|
|
362
413
|
end
|
|
363
414
|
|
|
415
|
+
data[:node_enabled_namespaces_query_result].each do |ns_data|
|
|
416
|
+
expect(metrics).to receive(:add)
|
|
417
|
+
.with(
|
|
418
|
+
"search_zoekt_node_enabled_namespaces",
|
|
419
|
+
ns_data["count"].to_i,
|
|
420
|
+
node_id: ns_data["node_id"],
|
|
421
|
+
node_name: ns_data["node_name"]
|
|
422
|
+
)
|
|
423
|
+
end
|
|
424
|
+
|
|
425
|
+
# Only rows with a known state integer are emitted; the nil-state row is skipped.
|
|
426
|
+
task_state_map = GitLab::Exporter::Database::ZoektCollector::ZOEKT_TASK_STATES
|
|
427
|
+
data[:node_tasks_query_result].each do |task_data|
|
|
428
|
+
next if task_data["state"].nil?
|
|
429
|
+
|
|
430
|
+
state_label = task_state_map[task_data["state"].to_i]
|
|
431
|
+
next if state_label.nil?
|
|
432
|
+
|
|
433
|
+
expect(metrics).to receive(:add)
|
|
434
|
+
.with(
|
|
435
|
+
"search_zoekt_node_tasks",
|
|
436
|
+
task_data["count"].to_i,
|
|
437
|
+
node_id: task_data["node_id"],
|
|
438
|
+
node_name: task_data["node_name"],
|
|
439
|
+
state: state_label
|
|
440
|
+
)
|
|
441
|
+
end
|
|
442
|
+
|
|
443
|
+
data[:indices_stale_used_storage_query_result].each do |stale_data|
|
|
444
|
+
expect(metrics).to receive(:add)
|
|
445
|
+
.with(
|
|
446
|
+
"search_zoekt_indices_with_stale_used_storage_bytes",
|
|
447
|
+
stale_data["count"].to_i
|
|
448
|
+
)
|
|
449
|
+
end
|
|
450
|
+
|
|
364
451
|
probe_db
|
|
365
452
|
end
|
|
453
|
+
|
|
454
|
+
context "when node_tasks_query_result contains nil state rows" do
|
|
455
|
+
it "skips rows where state is nil" do
|
|
456
|
+
nil_state_data = data.merge(
|
|
457
|
+
node_tasks_query_result: [{ "node_id" => "1", "node_name" => "zoekt-1", "state" => nil, "count" => "3" }],
|
|
458
|
+
node_enabled_namespaces_query_result: [],
|
|
459
|
+
indices_stale_used_storage_query_result: []
|
|
460
|
+
)
|
|
461
|
+
allow(collector).to receive(:run).and_return(nil_state_data)
|
|
462
|
+
|
|
463
|
+
expect(metrics).not_to receive(:add).with("search_zoekt_node_tasks", anything, anything)
|
|
464
|
+
|
|
465
|
+
described_class.new(metrics: metrics, collector: collector, **opts).probe_db
|
|
466
|
+
end
|
|
467
|
+
end
|
|
468
|
+
|
|
469
|
+
context "when node_tasks_query_result contains an unknown state integer" do
|
|
470
|
+
it "skips rows where state integer is not in ZOEKT_TASK_STATES" do
|
|
471
|
+
unknown_state_data = data.merge(
|
|
472
|
+
node_tasks_query_result: [{ "node_id" => "1", "node_name" => "zoekt-1", "state" => "99", "count" => "2" }],
|
|
473
|
+
node_enabled_namespaces_query_result: [],
|
|
474
|
+
indices_stale_used_storage_query_result: []
|
|
475
|
+
)
|
|
476
|
+
allow(collector).to receive(:run).and_return(unknown_state_data)
|
|
477
|
+
|
|
478
|
+
expect(metrics).not_to receive(:add).with("search_zoekt_node_tasks", anything, anything)
|
|
479
|
+
|
|
480
|
+
described_class.new(metrics: metrics, collector: collector, **opts).probe_db
|
|
481
|
+
end
|
|
482
|
+
end
|
|
366
483
|
end
|
|
367
484
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: gitlab-exporter
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 16.
|
|
4
|
+
version: 16.10.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Pablo Carranza
|
|
@@ -44,14 +44,14 @@ dependencies:
|
|
|
44
44
|
requirements:
|
|
45
45
|
- - '='
|
|
46
46
|
- !ruby/object:Gem::Version
|
|
47
|
-
version: 2.14.
|
|
47
|
+
version: 2.14.3
|
|
48
48
|
type: :runtime
|
|
49
49
|
prerelease: false
|
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
|
51
51
|
requirements:
|
|
52
52
|
- - '='
|
|
53
53
|
- !ruby/object:Gem::Version
|
|
54
|
-
version: 2.14.
|
|
54
|
+
version: 2.14.3
|
|
55
55
|
- !ruby/object:Gem::Dependency
|
|
56
56
|
name: pg
|
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -72,14 +72,14 @@ dependencies:
|
|
|
72
72
|
requirements:
|
|
73
73
|
- - '='
|
|
74
74
|
- !ruby/object:Gem::Version
|
|
75
|
-
version: 8.0.
|
|
75
|
+
version: 8.0.2
|
|
76
76
|
type: :runtime
|
|
77
77
|
prerelease: false
|
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
|
79
79
|
requirements:
|
|
80
80
|
- - '='
|
|
81
81
|
- !ruby/object:Gem::Version
|
|
82
|
-
version: 8.0.
|
|
82
|
+
version: 8.0.2
|
|
83
83
|
- !ruby/object:Gem::Dependency
|
|
84
84
|
name: quantile
|
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|