gitlab-exporter 13.0.3 → 13.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 772759981296901f1bd6ed3d38a9f98cf882ddbc851605c8dfb31b47aceed046
4
- data.tar.gz: 82f8e5ce3795f3349dd1cd053161302cf01eafe0c57485ec3074e7a8844606a9
3
+ metadata.gz: 1faa75a67146917f990466c949a63cc5b53c4c4280bcaae76601071a88c5fb0c
4
+ data.tar.gz: acd50e384eb4e03fdbf2ba20c8f030a47b886822a3e87cdddded7326790e5f25
5
5
  SHA512:
6
- metadata.gz: ed3d74142e8da3b4bf367c9bf26b91b1e55a42f904e494479797d1bc10f07324e0734247a271a44e1a6db6ca745dddaddf97688d0ba659ff3e27f3f927fa6973
7
- data.tar.gz: f0af44afc5e5c3d425bdfe09e1d846b550b53f9b1bc24cad7549715a8d18dc751f60dda5a8e7c934d1a0b6c2f0285ea533c64419deaab78087ef102c2161d40b
6
+ metadata.gz: 6dbab1d6bce735e67ced073506ca2e9ddf1111ccd1b4a6e45a2289ce5a0ed069f5f004c2cda272a298d6328f7fe0fbd96cff30d434c722f01c1f1448b49199ce
7
+ data.tar.gz: c625442a214949794bad144d38a511c96161af0261380cac50f509f421c7da03c6549b94059451322c9f23498ca33ccc9ce528c02f4874357c79b21bf9486e04
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- gitlab-exporter (13.0.3)
4
+ gitlab-exporter (13.2.0)
5
5
  connection_pool (= 2.2.5)
6
6
  faraday (~> 1.8.0)
7
7
  pg (= 1.5.3)
@@ -95,6 +95,8 @@ probes:
95
95
  opts:
96
96
  redis_url: "redis://localhost:6379"
97
97
  redis_enable_client: true
98
+ probe_namespaced: true
99
+ probe_non_namespaced: true
98
100
 
99
101
  ruby: &ruby
100
102
  class_name: RubyProber
@@ -8,7 +8,7 @@ module GitLab
8
8
  # This class works under the assumption you do COUNT(*) queries, define
9
9
  # queries in the QUERIES constant. If in doubt how these work, read
10
10
  # #construct_query
11
- class RowCountCollector < Base
11
+ class RowCountCollector < Base # rubocop:disable Metrics/ClassLength
12
12
  # We ignore mirrors with a next_execution_timestamp before
13
13
  # 2020-03-28 because this is when we stopped processing mirrors
14
14
  # for private projects on the free plan. Skipping those can
@@ -29,6 +29,15 @@ module GitLab
29
29
  check: "SELECT 1 FROM information_schema.tables WHERE table_name='plans'"
30
30
  }.freeze
31
31
 
32
+ CONTAINER_REPOSITORIES_CLEANUP_ENABLED_QUERY = {
33
+ select: :container_repositories,
34
+ joins: <<~SQL,
35
+ INNER JOIN container_expiration_policies
36
+ ON container_repositories.project_id = container_expiration_policies.project_id
37
+ SQL
38
+ where: "container_expiration_policies.enabled = TRUE"
39
+ }.freeze
40
+
32
41
  QUERIES = {
33
42
  mirrors_ready_to_sync: MIRROR_QUERY.merge( # EE only
34
43
  where: <<~SQL
@@ -117,6 +126,52 @@ module GitLab
117
126
  visibility_level: {},
118
127
  root: { definition: "(parent_id IS NULL)" }
119
128
  }
129
+ },
130
+ container_repositories: { select: :container_repositories },
131
+ container_repositories_delete_scheduled: { select: :container_repositories, where: "status = 0" },
132
+ container_repositories_delete_failed: { select: :container_repositories, where: "status = 1" },
133
+ container_repositories_delete_ongoing: { select: :container_repositories, where: "status = 2" },
134
+ container_repositories_delete_staled: {
135
+ select: :container_repositories,
136
+ where: "status = 2 AND delete_started_at < (NOW() - INTERVAL '30 minutes')"
137
+ },
138
+ container_repositories_cleanup_enabled: CONTAINER_REPOSITORIES_CLEANUP_ENABLED_QUERY,
139
+ container_repositories_cleanup_pending: CONTAINER_REPOSITORIES_CLEANUP_ENABLED_QUERY.merge(
140
+ where: <<~SQL
141
+ container_expiration_policies.enabled = TRUE
142
+ AND container_repositories.expiration_policy_cleanup_status IN (0, 1)
143
+ AND (container_repositories.expiration_policy_started_at IS NULL OR container_repositories.expiration_policy_started_at < container_expiration_policies.next_run_at)
144
+ AND (container_expiration_policies.next_run_at < NOW())
145
+ SQL
146
+ ),
147
+ container_repositories_cleanup_unfinished: CONTAINER_REPOSITORIES_CLEANUP_ENABLED_QUERY.merge(
148
+ where: <<~SQL
149
+ container_expiration_policies.enabled = TRUE
150
+ AND container_repositories.expiration_policy_cleanup_status = 2
151
+ SQL
152
+ ),
153
+ container_repositories_cleanup_unscheduled: CONTAINER_REPOSITORIES_CLEANUP_ENABLED_QUERY.merge(
154
+ where: <<~SQL
155
+ container_expiration_policies.enabled = TRUE
156
+ AND container_repositories.expiration_policy_cleanup_status = 0
157
+ SQL
158
+ ),
159
+ container_repositories_cleanup_scheduled: CONTAINER_REPOSITORIES_CLEANUP_ENABLED_QUERY.merge(
160
+ where: <<~SQL
161
+ container_expiration_policies.enabled = TRUE
162
+ AND container_repositories.expiration_policy_cleanup_status = 1
163
+ SQL
164
+ ),
165
+ container_repositories_cleanup_ongoing: {
166
+ select: :container_repositories,
167
+ where: "expiration_policy_cleanup_status = 3"
168
+ },
169
+ container_repositories_cleanup_staled: {
170
+ select: :container_repositories,
171
+ where: <<~SQL
172
+ expiration_policy_cleanup_status = 3
173
+ AND (expiration_policy_started_at < (NOW() - INTERVAL '35 minutes') OR expiration_policy_started_at IS NULL)
174
+ SQL
120
175
  }
121
176
  }.freeze
122
177
 
@@ -7,6 +7,7 @@ module GitLab
7
7
  # A prober for Sidekiq queues
8
8
  #
9
9
  # It takes the Redis URL Sidekiq is connected to
10
+ # rubocop:disable Metrics/ClassLength
10
11
  class SidekiqProber
11
12
  # The maximum depth (from the head) of each queue to probe. Probing the
12
13
  # entirety of a very large queue will take longer and run the risk of
@@ -37,35 +38,24 @@ module GitLab
37
38
  @opts = opts
38
39
  @metrics = metrics
39
40
  @logger = logger
41
+
42
+ @probe_namespaced = !!opts[:probe_namespaced] # rubocop:disable Style/DoubleNegation
43
+ @probe_non_namespaced = !!opts[:probe_non_namespaced] # rubocop:disable Style/DoubleNegation
44
+
45
+ # to maintain backward compatibility if the config is missing values
46
+ @probe_namespaced = true if opts[:probe_namespaced].nil? && opts[:probe_non_namespaced].nil?
40
47
  end
41
48
 
42
49
  def probe_stats
43
- with_sidekiq do
44
- stats = Sidekiq::Stats.new
45
-
46
- @metrics.add("sidekiq_jobs_processed_total", stats.processed)
47
- @metrics.add("sidekiq_jobs_failed_total", stats.failed)
48
- @metrics.add("sidekiq_jobs_enqueued_size", stats.enqueued)
49
- @metrics.add("sidekiq_jobs_scheduled_size", stats.scheduled_size)
50
- @metrics.add("sidekiq_jobs_retry_size", stats.retry_size)
51
- @metrics.add("sidekiq_jobs_dead_size", stats.dead_size)
52
-
53
- @metrics.add("sidekiq_default_queue_latency_seconds", stats.default_queue_latency)
54
- @metrics.add("sidekiq_processes_size", stats.processes_size)
55
- @metrics.add("sidekiq_workers_size", stats.workers_size)
56
- end
50
+ with_sidekiq { _probe_stats } if @probe_namespaced
51
+ with_sidekiq(false) { _probe_stats(false) } if @probe_non_namespaced
57
52
 
58
53
  self
59
54
  end
60
55
 
61
56
  def probe_queues
62
- with_sidekiq do
63
- Sidekiq::Queue.all.each do |queue|
64
- @metrics.add("sidekiq_queue_size", queue.size, name: queue.name)
65
- @metrics.add("sidekiq_queue_latency_seconds", queue.latency, name: queue.name)
66
- @metrics.add("sidekiq_queue_paused", queue.paused? ? 1 : 0, name: queue.name)
67
- end
68
- end
57
+ with_sidekiq { _probe_queues } if @probe_namespaced
58
+ with_sidekiq(false) { _probe_queues(false) } if @probe_non_namespaced
69
59
 
70
60
  self
71
61
  end
@@ -78,24 +68,8 @@ module GitLab
78
68
  end
79
69
 
80
70
  def probe_future_sets
81
- now = Time.now.to_f
82
- with_sidekiq do
83
- Sidekiq.redis do |conn|
84
- Sidekiq::Scheduled::SETS.each do |set|
85
- # Default to 0; if all jobs are due in the future, there is no "negative" delay.
86
- delay = 0
87
-
88
- _job, timestamp = conn.zrangebyscore(set, "-inf", now.to_s, limit: [0, 1], withscores: true).first
89
- delay = now - timestamp if timestamp
90
-
91
- @metrics.add("sidekiq_#{set}_set_processing_delay_seconds", delay)
92
-
93
- # zcount is O(log(N)) (prob. binary search), so is still quick even with large sets
94
- @metrics.add("sidekiq_#{set}_set_backlog_count",
95
- conn.zcount(set, "-inf", now.to_s))
96
- end
97
- end
98
- end
71
+ with_sidekiq { _probe_future_sets } if @probe_namespaced
72
+ with_sidekiq(false) { _probe_future_sets(false) } if @probe_non_namespaced
99
73
  end
100
74
 
101
75
  # Count worker classes present in Sidekiq queues. This only looks at the
@@ -105,57 +79,22 @@ module GitLab
105
79
  # will not have completely accurate statistics, but the probe performance
106
80
  # will also not degrade as the queue gets larger.
107
81
  def probe_jobs_limit
108
- with_sidekiq do
109
- job_stats = Hash.new(0)
110
-
111
- Sidekiq::Queue.all.each do |queue|
112
- Sidekiq.redis do |conn|
113
- conn.lrange("queue:#{queue.name}", 0, PROBE_JOBS_LIMIT).each do |job|
114
- job_class = Sidekiq.load_json(job)["class"]
115
-
116
- job_stats[job_class] += 1
117
- end
118
- end
119
- end
120
-
121
- job_stats.each do |class_name, count|
122
- @metrics.add("sidekiq_enqueued_jobs", count, name: class_name)
123
- end
124
- end
82
+ with_sidekiq { _probe_jobs_limit } if @probe_namespaced
83
+ with_sidekiq(false) { _probe_jobs_limit(false) } if @probe_non_namespaced
125
84
 
126
85
  self
127
86
  end
128
87
 
129
88
  def probe_workers
130
- with_sidekiq do
131
- worker_stats = Hash.new(0)
132
-
133
- Sidekiq::Workers.new.map do |_pid, _tid, work|
134
- job_klass = work["payload"]["class"]
135
-
136
- worker_stats[job_klass] += 1
137
- end
138
-
139
- worker_stats.each do |class_name, count|
140
- @metrics.add("sidekiq_running_jobs", count, name: class_name)
141
- end
142
- end
89
+ with_sidekiq { _probe_workers } if @probe_namespaced
90
+ with_sidekiq(false) { _probe_workers(false) } if @probe_non_namespaced
143
91
 
144
92
  self
145
93
  end
146
94
 
147
95
  def probe_retries
148
- with_sidekiq do
149
- retry_stats = Hash.new(0)
150
-
151
- Sidekiq::RetrySet.new.map do |job|
152
- retry_stats[job.klass] += 1
153
- end
154
-
155
- retry_stats.each do |class_name, count|
156
- @metrics.add("sidekiq_to_be_retried_jobs", count, name: class_name)
157
- end
158
- end
96
+ with_sidekiq { _probe_retries } if @probe_namespaced
97
+ with_sidekiq(false) { _probe_retries(false) } if @probe_non_namespaced
159
98
 
160
99
  self
161
100
  end
@@ -177,12 +116,106 @@ module GitLab
177
116
 
178
117
  private
179
118
 
180
- def with_sidekiq
119
+ def _probe_workers(namespace = true)
120
+ labels = add_namespace_labels? ? { namespaced: namespace } : {}
121
+ worker_stats = Hash.new(0)
122
+
123
+ Sidekiq::Workers.new.map do |_pid, _tid, work|
124
+ job_klass = work["payload"]["class"]
125
+
126
+ worker_stats[job_klass] += 1
127
+ end
128
+
129
+ worker_stats.each do |class_name, count|
130
+ @metrics.add("sidekiq_running_jobs", count, **labels.merge({ name: class_name }))
131
+ end
132
+ end
133
+
134
+ def _probe_future_sets(namespace = true)
135
+ labels = add_namespace_labels? ? { namespaced: namespace } : {}
136
+ now = Time.now.to_f
137
+ Sidekiq.redis do |conn|
138
+ Sidekiq::Scheduled::SETS.each do |set|
139
+ # Default to 0; if all jobs are due in the future, there is no "negative" delay.
140
+ delay = 0
141
+
142
+ _job, timestamp = conn.zrangebyscore(set, "-inf", now.to_s, limit: [0, 1], withscores: true).first
143
+ delay = now - timestamp if timestamp
144
+
145
+ @metrics.add("sidekiq_#{set}_set_processing_delay_seconds", delay, **labels)
146
+
147
+ # zcount is O(log(N)) (prob. binary search), so is still quick even with large sets
148
+ @metrics.add("sidekiq_#{set}_set_backlog_count",
149
+ conn.zcount(set, "-inf", now.to_s), **labels)
150
+ end
151
+ end
152
+ end
153
+
154
+ def _probe_stats(namespace = true)
155
+ stats = Sidekiq::Stats.new
156
+ labels = add_namespace_labels? ? { namespaced: namespace } : {}
157
+
158
+ @metrics.add("sidekiq_jobs_processed_total", stats.processed, **labels)
159
+ @metrics.add("sidekiq_jobs_failed_total", stats.failed, **labels)
160
+ @metrics.add("sidekiq_jobs_enqueued_size", stats.enqueued, **labels)
161
+ @metrics.add("sidekiq_jobs_scheduled_size", stats.scheduled_size, **labels)
162
+ @metrics.add("sidekiq_jobs_retry_size", stats.retry_size, **labels)
163
+ @metrics.add("sidekiq_jobs_dead_size", stats.dead_size, **labels)
164
+
165
+ @metrics.add("sidekiq_default_queue_latency_seconds", stats.default_queue_latency, **labels)
166
+ @metrics.add("sidekiq_processes_size", stats.processes_size, **labels)
167
+ @metrics.add("sidekiq_workers_size", stats.workers_size, **labels)
168
+ end
169
+
170
+ def _probe_queues(namespace = true)
171
+ Sidekiq::Queue.all.each do |queue|
172
+ labels = { name: queue.name }
173
+ labels[:namespaced] = namespace if add_namespace_labels?
174
+
175
+ @metrics.add("sidekiq_queue_size", queue.size, **labels)
176
+ @metrics.add("sidekiq_queue_latency_seconds", queue.latency, **labels)
177
+ @metrics.add("sidekiq_queue_paused", queue.paused? ? 1 : 0, **labels)
178
+ end
179
+ end
180
+
181
+ def _probe_jobs_limit(namespace = true)
182
+ labels = add_namespace_labels? ? { namespaced: namespace } : {}
183
+ job_stats = Hash.new(0)
184
+
185
+ Sidekiq::Queue.all.each do |queue|
186
+ Sidekiq.redis do |conn|
187
+ conn.lrange("queue:#{queue.name}", 0, PROBE_JOBS_LIMIT).each do |job|
188
+ job_class = Sidekiq.load_json(job)["class"]
189
+
190
+ job_stats[job_class] += 1
191
+ end
192
+ end
193
+ end
194
+
195
+ job_stats.each do |class_name, count|
196
+ @metrics.add("sidekiq_enqueued_jobs", count, **labels.merge({ name: class_name }))
197
+ end
198
+ end
199
+
200
+ def _probe_retries(namespace = true)
201
+ labels = add_namespace_labels? ? { namespaced: namespace } : {}
202
+ retry_stats = Hash.new(0)
203
+
204
+ Sidekiq::RetrySet.new.map do |job|
205
+ retry_stats[job.klass] += 1
206
+ end
207
+
208
+ retry_stats.each do |class_name, count|
209
+ @metrics.add("sidekiq_to_be_retried_jobs", count, **labels.merge({ name: class_name }))
210
+ end
211
+ end
212
+
213
+ def with_sidekiq(namespaced = true)
181
214
  # TODO: this is not concurrent safe as we change global context
182
215
  # It means that we are unable to use many different sidekiq's
183
216
  # which is not a problem as of now
184
217
  Sidekiq.configure_client do |config|
185
- config.redis = self.class.connection_pool[redis_options]
218
+ config.redis = self.class.connection_pool[redis_options(namespaced)]
186
219
  end
187
220
 
188
221
  return unless connected?
@@ -190,14 +223,14 @@ module GitLab
190
223
  yield
191
224
  end
192
225
 
193
- def redis_options
226
+ def redis_options(namespaced = true)
194
227
  options = {
195
228
  url: @opts[:redis_url],
196
- namespace: "resque:gitlab",
197
229
  connect_timeout: 1,
198
230
  reconnect_attempts: 0
199
231
  }
200
232
 
233
+ options[:namespace] = "resque:gitlab" if namespaced
201
234
  options[:id] = nil unless redis_enable_client?
202
235
  options
203
236
  end
@@ -218,6 +251,11 @@ module GitLab
218
251
  @logger&.error "Error connecting to the Redis: #{e}"
219
252
  @connected = false
220
253
  end
254
+
255
+ def add_namespace_labels?
256
+ @probe_namespaced && @probe_non_namespaced
257
+ end
221
258
  end
259
+ # rubocop:enable Metrics/ClassLength
222
260
  end
223
261
  end
@@ -1,5 +1,5 @@
1
1
  module GitLab
2
2
  module Exporter
3
- VERSION = "13.0.3".freeze
3
+ VERSION = "13.2.0".freeze
4
4
  end
5
5
  end
@@ -0,0 +1,64 @@
1
+ require "spec_helper"
2
+ require "gitlab_exporter/sidekiq"
3
+
4
+ describe GitLab::Exporter::SidekiqProber do
5
+ let(:probe_list) { %w[probe_stats probe_queues probe_future_sets probe_jobs_limit probe_workers probe_retries] }
6
+ let(:opt) { {} }
7
+ let(:probe) { described_class.new(**opt) }
8
+
9
+ before do
10
+ # stub connection as we want check if the probe methods are called
11
+ # with the right namespace argument
12
+ allow(probe).to receive(:connected?).and_return(true)
13
+ end
14
+
15
+ context "when no namespace options are defined" do
16
+ it "defaults to probing non-namespaced keys only" do
17
+ probe_list.each do |probe_type|
18
+ expect(probe).not_to receive("_#{probe_type}".to_sym).with(false)
19
+ expect(probe).to receive("_#{probe_type}".to_sym).with(no_args)
20
+
21
+ probe.send(probe_type)
22
+ end
23
+ end
24
+ end
25
+
26
+ context "when probing both namespaces" do
27
+ let(:opt) { { probe_non_namespaced: true, probe_namespaced: true } }
28
+
29
+ it "probes both namespaces" do
30
+ probe_list.each do |probe_type|
31
+ expect(probe).to receive("_#{probe_type}".to_sym).with(false)
32
+ expect(probe).to receive("_#{probe_type}".to_sym).with(no_args)
33
+
34
+ probe.send(probe_type)
35
+ end
36
+ end
37
+ end
38
+
39
+ context "when probing non-namespaced only" do
40
+ let(:opt) { { probe_non_namespaced: true } }
41
+
42
+ it "probes non-namespaced only" do
43
+ probe_list.each do |probe_type|
44
+ expect(probe).to receive("_#{probe_type}".to_sym).with(false)
45
+ expect(probe).not_to receive("_#{probe_type}".to_sym).with(no_args)
46
+
47
+ probe.send(probe_type)
48
+ end
49
+ end
50
+ end
51
+
52
+ context "when probing namespace only" do
53
+ let(:opt) { { probe_namespaced: true } }
54
+
55
+ it "probes namespaced only" do
56
+ probe_list.each do |probe_type|
57
+ expect(probe).not_to receive("_#{probe_type}".to_sym).with(false)
58
+ expect(probe).to receive("_#{probe_type}".to_sym).with(no_args)
59
+
60
+ probe.send(probe_type)
61
+ end
62
+ end
63
+ end
64
+ 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: 13.0.3
4
+ version: 13.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pablo Carranza
@@ -235,6 +235,7 @@ files:
235
235
  - spec/memstats_spec.rb
236
236
  - spec/prometheus_metrics_spec.rb
237
237
  - spec/ruby_spec.rb
238
+ - spec/sidekiq_spec.rb
238
239
  - spec/spec_helper.rb
239
240
  - spec/util_spec.rb
240
241
  homepage: http://gitlab.com
@@ -273,5 +274,6 @@ test_files:
273
274
  - spec/memstats_spec.rb
274
275
  - spec/prometheus_metrics_spec.rb
275
276
  - spec/ruby_spec.rb
277
+ - spec/sidekiq_spec.rb
276
278
  - spec/spec_helper.rb
277
279
  - spec/util_spec.rb