gitlab-labkit 1.15.1 → 1.17.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 55b7255672a78e7ce050b17dee4a50b0c9ceadd2093986dc0d5d3822076423b0
4
- data.tar.gz: fe70e4a910d27fa2d8d1a61216825bb811a8a43b9fbd75d4c0bd32220e8551b3
3
+ metadata.gz: 7177b8f9210f24958a14c92f17ad35cc8d4ad7732e04a9407569e865ef2bf364
4
+ data.tar.gz: 688c6dd7b7c1a8c1062a1e17c534fa833f86053aff8945ab4d296c2223784711
5
5
  SHA512:
6
- metadata.gz: 38e4de6ac2d9e677a269f757e4c01a1bb1d9e3ce4cec0065512a03d07650339ae6e5f27415be76ba02cc316c56e5fd8d4922389e856da152be527800a6f58de6
7
- data.tar.gz: 613915d89f71db61f3a984ae979d704753c9284d196dc7f31c92426b0f037ded2c0140ea9fc778dbbd42bb7c0ccfa0884b3d67e3ebc38b791bc84edfa6cb852d
6
+ metadata.gz: 2b54fe16d5c1cfe12c704a4e84cdea8dece8155dcaa04132c9e52442bec03e6338f6a4bcb065aa87931aa2fedcbd2ecb5ecec390b00d110267ee54e95a5c694c
7
+ data.tar.gz: 8abc6a48c3f1b138a84f8502e5c4300bec5f05f98b0c8defb4280d12b1d424f70cf64e0afe3cc344ce61ceae145e9aaf11f252d9611da1e5c6a1f25aa37fe867
data/.gitlab-ci.yml CHANGED
@@ -19,13 +19,13 @@ include:
19
19
  # It includes standard checks, gitlab-scanners, validations and release processes
20
20
  # common to all projects using this template library.
21
21
  # see https://gitlab.com/gitlab-com/gl-infra/common-ci-tasks/-/blob/main/templates/standard.md
22
- - component: $CI_SERVER_FQDN/gitlab-com/gl-infra/common-ci-tasks/standard-build@v3.12
22
+ - component: $CI_SERVER_FQDN/gitlab-com/gl-infra/common-ci-tasks/standard-build@v3.23
23
23
 
24
24
  # Runs rspec tests and rubocop on the project
25
25
  # see https://gitlab.com/gitlab-com/gl-infra/common-ci-tasks/-/blob/main/templates/ruby.md
26
- - component: $CI_SERVER_FQDN/gitlab-com/gl-infra/common-ci-tasks/ruby-build@v3.12
26
+ - component: $CI_SERVER_FQDN/gitlab-com/gl-infra/common-ci-tasks/ruby-build@v3.23
27
27
 
28
- - component: $CI_SERVER_FQDN/gitlab-com/gl-infra/common-ci-tasks/danger@v3.12
28
+ - component: $CI_SERVER_FQDN/gitlab-com/gl-infra/common-ci-tasks/danger@v3.23
29
29
 
30
30
  ruby-versions:
31
31
  extends: rspec
@@ -25,7 +25,7 @@ repos:
25
25
  # Documentation available at
26
26
  # https://gitlab.com/gitlab-com/gl-infra/common-ci-tasks/-/blob/main/docs/pre-commit.md
27
27
  - repo: https://gitlab.com/gitlab-com/gl-infra/common-ci-tasks
28
- rev: v3.12 # renovate:managed
28
+ rev: v3.23 # renovate:managed
29
29
 
30
30
  hooks:
31
31
  - id: shellcheck # Run shellcheck for changed Shell files
@@ -24,6 +24,7 @@ module Labkit
24
24
  rescue StandardError => e
25
25
  # Intentionally broad: fail-open applies to any unexpected error (network,
26
26
  # timeout, OOM) not only Redis protocol errors.
27
+ report_error_metrics
27
28
  log_error(e, identifier)
28
29
  Result.new(matched: false, error: true, action: :allow)
29
30
  end
@@ -34,9 +35,12 @@ module Labkit
34
35
  @rules.each do |rule|
35
36
  next unless rule_matches?(rule, identifier)
36
37
 
37
- return evaluate_rule(rule, identifier)
38
+ result = evaluate_rule(rule, identifier)
39
+ report_matched_metrics(result)
40
+ return result
38
41
  end
39
42
 
43
+ report_unmatched_metrics
40
44
  Result.new(matched: false, action: :allow)
41
45
  end
42
46
 
@@ -53,7 +57,10 @@ module Labkit
53
57
  exceeded = count > resolved_limit
54
58
  action = exceeded ? rule.action : :allow
55
59
 
56
- Result.new(matched: true, exceeded: exceeded, action: action, rule: rule)
60
+ Result.new(
61
+ matched: true, exceeded: exceeded, action: action, rule: rule,
62
+ resolved_limit: resolved_limit, resolved_period: resolved_period
63
+ )
57
64
  end
58
65
 
59
66
  def build_redis_key(rule, identifier)
@@ -85,10 +92,12 @@ module Labkit
85
92
  end
86
93
 
87
94
  def incr_with_ttl(redis_key, period)
88
- count = @redis.incr(redis_key)
89
- # Set expiry only on first write to avoid resetting TTL on each call
90
- @redis.expire(redis_key, period) if count == 1
91
- count
95
+ @redis.with do |conn|
96
+ count = conn.incr(redis_key)
97
+ # Set expiry only on first write to avoid resetting TTL on each call
98
+ conn.expire(redis_key, period) if count == 1
99
+ count
100
+ end
92
101
  end
93
102
 
94
103
  def log_error(error, identifier)
@@ -99,6 +108,34 @@ module Labkit
99
108
  identifier: identifier&.to_h
100
109
  )
101
110
  end
111
+
112
+ def report_matched_metrics(result)
113
+ Metrics.calls_total.increment(
114
+ rate_limiter: @name,
115
+ rule: result.rule.name,
116
+ action: result.action.to_s
117
+ )
118
+ Metrics.limit_gauge.set(
119
+ { rate_limiter: @name, rule: result.rule.name },
120
+ result.resolved_limit
121
+ )
122
+ Metrics.period_gauge.set(
123
+ { rate_limiter: @name, rule: result.rule.name },
124
+ result.resolved_period
125
+ )
126
+ end
127
+
128
+ def report_unmatched_metrics
129
+ Metrics.calls_total.increment(
130
+ rate_limiter: @name,
131
+ rule: "unmatched",
132
+ action: "allow"
133
+ )
134
+ end
135
+
136
+ def report_error_metrics
137
+ Metrics.errors_total.increment(rate_limiter: @name)
138
+ end
102
139
  end
103
140
  end
104
141
  end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Labkit
4
+ module RateLimit
5
+ module Metrics
6
+ module_function
7
+
8
+ def calls_total
9
+ Labkit::Metrics::Client.counter(
10
+ :gitlab_labkit_rate_limiter_calls_total,
11
+ 'Total number of successful rate limit checks',
12
+ { rate_limiter: nil, rule: nil, action: nil }
13
+ )
14
+ end
15
+
16
+ def errors_total
17
+ Labkit::Metrics::Client.counter(
18
+ :gitlab_labkit_rate_limiter_errors_total,
19
+ 'Total number of rate limit check errors',
20
+ { rate_limiter: nil }
21
+ )
22
+ end
23
+
24
+ def limit_gauge
25
+ Labkit::Metrics::Client.gauge(
26
+ :gitlab_labkit_rate_limiter_limit,
27
+ 'The configured rate limit threshold',
28
+ { rate_limiter: nil, rule: nil },
29
+ :max
30
+ )
31
+ end
32
+
33
+ def period_gauge
34
+ Labkit::Metrics::Client.gauge(
35
+ :gitlab_labkit_rate_limiter_period_seconds,
36
+ 'The configured rate limit period in seconds',
37
+ { rate_limiter: nil, rule: nil },
38
+ :max
39
+ )
40
+ end
41
+ end
42
+ end
43
+ end
@@ -3,18 +3,22 @@
3
3
  module Labkit
4
4
  module RateLimit
5
5
  # Result is the return value of Limiter#check.
6
- # matched? - true if a rule's match conditions were satisfied
7
- # exceeded? - true if the matched rule's counter exceeded its limit
8
- # action - the outcome: what the caller should do
9
- # :block = rule matched, exceeded, rule configured to block
10
- # :log = rule matched, exceeded, rule configured to log only
11
- # :allow = rule matched but count within limit, or
12
- # no rule matched, or error (fail-open)
13
- # The rule's configured action is available via rule.action
14
- # rule - the matched Rule object (nil when matched? is false)
15
- # error? - true if Redis was unavailable; result fails open (exceeded? is false)
16
- Result = Data.define(:matched, :exceeded, :action, :rule, :error) do
17
- def initialize(matched:, action:, exceeded: false, rule: nil, error: false)
6
+ # matched? - true if a rule's match conditions were satisfied
7
+ # exceeded? - true if the matched rule's counter exceeded its limit
8
+ # action - the outcome: what the caller should do
9
+ # :block = rule matched, exceeded, rule configured to block
10
+ # :log = rule matched, exceeded, rule configured to log only
11
+ # :allow = rule matched but count within limit, or
12
+ # no rule matched, or error (fail-open)
13
+ # The rule's configured action is available via rule.action
14
+ # rule - the matched Rule object (nil when matched? is false)
15
+ # error? - true if Redis was unavailable; result fails open (exceeded? is false)
16
+ # resolved_limit - the resolved limit value as Integer (nil when matched? is false or error)
17
+ # resolved_period - the resolved period value as Integer (nil when matched? is false or error)
18
+ Result = Data.define(:matched, :exceeded, :action, :rule, :error, :resolved_limit, :resolved_period) do
19
+ def initialize(
20
+ matched:, action:, exceeded: false, rule: nil, error: false,
21
+ resolved_limit: nil, resolved_period: nil)
18
22
  super
19
23
  end
20
24
 
@@ -6,7 +6,7 @@ module Labkit
6
6
  #
7
7
  # @example Configuration (e.g. in a Rails initializer)
8
8
  # Labkit::RateLimit.configure do |c|
9
- # c.redis = Redis.current
9
+ # c.redis = ConnectionPool.new { Redis.new } # must respond to .with { |conn| }
10
10
  # c.logger = Labkit::Logging::JsonLogger.new($stdout)
11
11
  # end
12
12
  #
@@ -23,6 +23,7 @@ module Labkit
23
23
  autoload :Rule, "labkit/rate_limit/rule"
24
24
  autoload :Evaluator, "labkit/rate_limit/evaluator"
25
25
  autoload :Limiter, "labkit/rate_limit/limiter"
26
+ autoload :Metrics, "labkit/rate_limit/metrics"
26
27
 
27
28
  class << self
28
29
  def configure
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gitlab-labkit
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.15.1
4
+ version: 1.17.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Newdigate
@@ -603,6 +603,7 @@ files:
603
603
  - lib/labkit/rate_limit/evaluator.rb
604
604
  - lib/labkit/rate_limit/identifier.rb
605
605
  - lib/labkit/rate_limit/limiter.rb
606
+ - lib/labkit/rate_limit/metrics.rb
606
607
  - lib/labkit/rate_limit/result.rb
607
608
  - lib/labkit/rate_limit/rule.rb
608
609
  - lib/labkit/rspec/README.md