gitlab-labkit 4.2.0 → 4.3.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: 1092d43de21613288d5ed802539e0eeab87bee9e8975e223f78aa29116f6cb2d
4
- data.tar.gz: 7a4953fd47e09da3cc2f4657cbe563648fec3ea0f0ae63d47e0bf49610b3b39d
3
+ metadata.gz: 63bc67bcb0041f919748393e6ace8e96d29bcd88e2c5b0e08b8baad8968670b1
4
+ data.tar.gz: 5bd9b55ece983433e125c7cd087030d740647101a48e9af1f97f1aa68ed51db4
5
5
  SHA512:
6
- metadata.gz: 5f885a5454c551e8adc0fd4ad8ca99c81bbc438a61b4787e122fae02f966062d8006c9766cb98f73d2e4c4465a3d5c3a3dd9fe13e44eb76ca21816b1c60571a1
7
- data.tar.gz: b11dc29d11ea325ea787af6ad1827bb9963146427d0930c38089b4f1901d21e1faca20437f52cd4a4f8de7b7de85fc0de2932cdcc5f8279c8e9fbe1cd77f711b
6
+ metadata.gz: 61d298ae9cb7c584d6e9979fbdb25fc112b705a8f1c0022825192e2759356408cbe6c03caf339ae5140220e325dc90532f24775a1f76decf1eb1547269cf3aba
7
+ data.tar.gz: 3755c6de56d994d081210d1cae48430d9938420c6ac33932ce672f3c20eee86457742ce75966bbd2163c09807a4b4e4c72e82579f8eba262e25cb1b79204ce1f
@@ -12,6 +12,12 @@ module Labkit
12
12
  CHAR_VALUE_MAX_LENGTH = 200
13
13
  MISSING_VALUE_SENTINEL = "_unknown_"
14
14
 
15
+ # Carries the rule in flight out of the rule loop and into the rescue in
16
+ # #check/#peek, where the error is seen but the rule is out of scope.
17
+ # Per call, not instance state: one Evaluator serves concurrent requests.
18
+ # Only read while an exception unwinds; nil means no rule was in flight.
19
+ RuleCursor = Struct.new(:rule)
20
+
15
21
  # Atomic increment-with-TTL Lua script. The whole script runs as one
16
22
  # operation from Redis's perspective, so there is no window between
17
23
  # the increment and EXPIRE that can leak a key without TTL.
@@ -68,12 +74,13 @@ module Labkit
68
74
  end
69
75
 
70
76
  def check(identifier, cost: 1, rule_context: nil)
71
- check_rules(identifier, cost, rule_context)
77
+ cursor = RuleCursor.new
78
+ check_rules(identifier, cost, rule_context, cursor)
72
79
  rescue StandardError => e
73
80
  # Intentionally broad: fail-open applies to any unexpected error (network,
74
81
  # timeout, OOM) not only Redis protocol errors.
75
82
  report_error_metrics
76
- log_error(e, identifier)
83
+ log_error(e, identifier, cursor.rule)
77
84
  Result.error
78
85
  end
79
86
 
@@ -81,10 +88,11 @@ module Labkit
81
88
  # shape; the underlying Redis counter is not mutated and the TTL is not
82
89
  # extended. A missing Redis key is treated as count=0 (matched, not exceeded).
83
90
  def peek(identifier, rule_context: nil)
84
- peek_rules(identifier, rule_context)
91
+ cursor = RuleCursor.new
92
+ peek_rules(identifier, rule_context, cursor)
85
93
  rescue StandardError => e
86
94
  report_error_metrics
87
- log_error(e, identifier)
95
+ log_error(e, identifier, cursor.rule)
88
96
  Result.error
89
97
  end
90
98
 
@@ -117,11 +125,14 @@ module Labkit
117
125
  # counters were incremented. No blocking verdict is lost that way - a
118
126
  # :limit rule over its limit returns before any later rule can raise -
119
127
  # but a rule declared after the failing one loses its chance to block.
120
- # The request is counted and allowed.
121
- def check_rules(identifier, cost, rule_context)
128
+ # The request is counted and allowed. +cursor+ is set before the match, so
129
+ # a raise from the match itself (e.g. Regexp::TimeoutError) is attributed.
130
+ def check_rules(identifier, cost, rule_context, cursor)
122
131
  result = Result.new
123
132
 
124
133
  @rules.each do |rule|
134
+ cursor.rule = rule
135
+
125
136
  next unless rule_matches?(rule, identifier)
126
137
 
127
138
  if rule.action == :skip
@@ -141,6 +152,9 @@ module Labkit
141
152
  return result if result.block?
142
153
  end
143
154
 
155
+ # The loop is done, so anything raised from here on belongs to no rule.
156
+ cursor.rule = nil
157
+
144
158
  report_unmatched_metrics unless result.matched?
145
159
  result
146
160
  end
@@ -152,10 +166,12 @@ module Labkit
152
166
  # peek does not need the count_distinct identifier key - it reads SCARD on
153
167
  # the rule-keyed compound key, which contains the cardinality across all
154
168
  # members. So missing-key fail-open does not apply here.
155
- def peek_rules(identifier, rule_context)
169
+ def peek_rules(identifier, rule_context, cursor)
156
170
  result = Result.new
157
171
 
158
172
  @rules.each do |rule|
173
+ cursor.rule = rule
174
+
159
175
  next unless rule_matches?(rule, identifier)
160
176
 
161
177
  return result.skip!(rule) if rule.action == :skip
@@ -164,6 +180,8 @@ module Labkit
164
180
  return result if result.block?
165
181
  end
166
182
 
183
+ cursor.rule = nil
184
+
167
185
  result
168
186
  end
169
187
 
@@ -319,9 +337,14 @@ module Labkit
319
337
  end
320
338
  end
321
339
 
322
- def log_error(error, identifier)
340
+ # rule is nil when the error was raised with no rule in flight - before the
341
+ # loop reached one, or after it finished - so the field is logged as null
342
+ # rather than omitted, the same way identifier is. A named rule is the rule
343
+ # whose match or evaluation raised.
344
+ def log_error(error, identifier, rule = nil)
323
345
  @logger.warn(
324
346
  name: @name,
347
+ rule: rule&.name,
325
348
  Labkit::Fields::ERROR_TYPE => "rate_limit_error",
326
349
  Labkit::Fields::CLASS_NAME => error.class.to_s,
327
350
  Labkit::Fields::ERROR_MESSAGE => error.message,
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: 4.2.0
4
+ version: 4.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Newdigate