posthog-ruby 3.15.0 → 3.15.2
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/lib/posthog/backoff_policy.rb +1 -1
- data/lib/posthog/client.rb +3 -1
- data/lib/posthog/feature_flags.rb +24 -7
- data/lib/posthog/send_worker.rb +1 -0
- data/lib/posthog/transport.rb +29 -6
- data/lib/posthog/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 87c4c21f1799cc12f7e4792f728194091a4e3abfe28fa26315469c43cbefd6ba
|
|
4
|
+
data.tar.gz: 4fa4b2455dcf53c3e805616af1e5eb5ae266729f90832f4c4491147c6626f71d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b1fce8fb104ff14b8564649222d729fdd6feda8d3382119eee8c034663108edb972932aaee7d17de6d87911816986e55236a554ce19ba107e032bc04f4fdab7d
|
|
7
|
+
data.tar.gz: b95fecee03f14f695410f76230e8e1284286c4835cd8f4c2ef66779fc57a27bda7e7c3e892a989207ebd85d1f9531a6034a041dade066d776707ae03c7227661
|
data/lib/posthog/client.rb
CHANGED
|
@@ -67,6 +67,8 @@ module PostHog
|
|
|
67
67
|
# in seconds. Defaults to 30.
|
|
68
68
|
# @option opts [Integer] :feature_flag_request_timeout_seconds How long to wait for feature flag evaluation,
|
|
69
69
|
# in seconds. Defaults to 3.
|
|
70
|
+
# @option opts [Integer] :max_retries How many times to retry batch uploads after the first send attempt.
|
|
71
|
+
# Defaults to the transport default. Set to 0 to disable retrying.
|
|
70
72
|
# @option opts [Integer] :feature_flag_request_max_retries How many times to retry a flag request after a
|
|
71
73
|
# transient network error. Each retry sleeps on the calling thread before retrying, so this adds to
|
|
72
74
|
# worst-case latency. Defaults to 1. Set to 0 to disable retrying.
|
|
@@ -110,7 +112,7 @@ module PostHog
|
|
|
110
112
|
@transport = Transport.new(
|
|
111
113
|
api_host: opts[:host],
|
|
112
114
|
skip_ssl_verification: opts[:skip_ssl_verification],
|
|
113
|
-
retries: 3,
|
|
115
|
+
retries: opts.key?(:max_retries) ? opts[:max_retries].to_i + 1 : 3,
|
|
114
116
|
compress_request: opts[:compress_request]
|
|
115
117
|
)
|
|
116
118
|
@sync_lock = Mutex.new
|
|
@@ -36,8 +36,9 @@ module PostHog
|
|
|
36
36
|
# @param feature_flag_request_timeout_seconds [Integer] Timeout for feature flag requests.
|
|
37
37
|
# @param on_error [Proc, nil] Callback invoked as `on_error.call(status, error)`.
|
|
38
38
|
# @param flag_definition_cache_provider [Object, nil] Optional {FlagDefinitionCacheProvider} implementation.
|
|
39
|
-
# @param feature_flag_request_max_retries [Integer, nil] Retries after a transient network error
|
|
40
|
-
# request. Defaults to {Defaults::FeatureFlags::FLAG_REQUEST_MAX_RETRIES}.
|
|
39
|
+
# @param feature_flag_request_max_retries [Integer, nil] Retries after a transient network error or retryable
|
|
40
|
+
# HTTP response status on a flag request. Defaults to {Defaults::FeatureFlags::FLAG_REQUEST_MAX_RETRIES}.
|
|
41
|
+
# Set to 0 to disable retrying.
|
|
41
42
|
def initialize(
|
|
42
43
|
polling_interval,
|
|
43
44
|
personal_api_key,
|
|
@@ -150,7 +151,7 @@ module PostHog
|
|
|
150
151
|
group_properties: group_properties
|
|
151
152
|
}
|
|
152
153
|
request_data[:flag_keys_to_evaluate] = flag_keys if flag_keys && !flag_keys.empty?
|
|
153
|
-
request_data[:geoip_disable] =
|
|
154
|
+
request_data[:geoip_disable] = disable_geoip unless disable_geoip.nil?
|
|
154
155
|
|
|
155
156
|
flags_response = _request_feature_flag_evaluation(request_data)
|
|
156
157
|
|
|
@@ -1234,7 +1235,12 @@ module PostHog
|
|
|
1234
1235
|
data['token'] = @project_api_key
|
|
1235
1236
|
req.body = data.to_json
|
|
1236
1237
|
|
|
1237
|
-
_request(
|
|
1238
|
+
_request(
|
|
1239
|
+
uri,
|
|
1240
|
+
req,
|
|
1241
|
+
@feature_flag_request_timeout_seconds,
|
|
1242
|
+
retry_status_codes: RETRYABLE_FLAGS_REQUEST_STATUS_CODES
|
|
1243
|
+
)
|
|
1238
1244
|
end
|
|
1239
1245
|
|
|
1240
1246
|
def _request_remote_config_payload(flag_key)
|
|
@@ -1256,14 +1262,15 @@ module PostHog
|
|
|
1256
1262
|
Errno::ECONNRESET,
|
|
1257
1263
|
EOFError
|
|
1258
1264
|
].freeze
|
|
1265
|
+
RETRYABLE_FLAGS_REQUEST_STATUS_CODES = [502, 504].freeze
|
|
1259
1266
|
|
|
1260
|
-
def _request(uri, request_object, timeout = nil, include_etag: false)
|
|
1267
|
+
def _request(uri, request_object, timeout = nil, include_etag: false, retry_status_codes: [])
|
|
1261
1268
|
request_object['User-Agent'] = "posthog-ruby/#{PostHog::VERSION}"
|
|
1262
1269
|
request_timeout = timeout || 10
|
|
1263
1270
|
backoff_policy = nil
|
|
1264
1271
|
attempts = 0
|
|
1265
1272
|
|
|
1266
|
-
|
|
1273
|
+
loop do
|
|
1267
1274
|
attempts += 1
|
|
1268
1275
|
Net::HTTP.start(
|
|
1269
1276
|
uri.hostname,
|
|
@@ -1277,6 +1284,16 @@ module PostHog
|
|
|
1277
1284
|
status_code = res.code.to_i
|
|
1278
1285
|
etag = include_etag ? res['ETag'] : nil
|
|
1279
1286
|
|
|
1287
|
+
if retry_status_codes.include?(status_code) && attempts <= @feature_flag_request_max_retries
|
|
1288
|
+
backoff_policy ||= BackoffPolicy.new
|
|
1289
|
+
interval = backoff_policy.next_interval.to_f / 1000
|
|
1290
|
+
logger.debug(
|
|
1291
|
+
"Retrying request to #{_mask_tokens_in_url(uri.to_s)} after HTTP #{status_code} (attempt #{attempts})"
|
|
1292
|
+
)
|
|
1293
|
+
sleep(interval)
|
|
1294
|
+
next
|
|
1295
|
+
end
|
|
1296
|
+
|
|
1280
1297
|
# Handle 304 Not Modified - return special response indicating no change
|
|
1281
1298
|
if status_code == 304
|
|
1282
1299
|
logger.debug("#{request_object.method} #{_mask_tokens_in_url(uri.to_s)} returned 304 Not Modified")
|
|
@@ -1304,7 +1321,7 @@ module PostHog
|
|
|
1304
1321
|
interval = backoff_policy.next_interval.to_f / 1000
|
|
1305
1322
|
logger.debug("Retrying request to #{_mask_tokens_in_url(uri.to_s)} after #{e.class} (attempt #{attempts})")
|
|
1306
1323
|
sleep(interval)
|
|
1307
|
-
|
|
1324
|
+
next
|
|
1308
1325
|
end
|
|
1309
1326
|
logger.debug("Unable to complete request to #{_mask_tokens_in_url(uri.to_s)}")
|
|
1310
1327
|
raise
|
data/lib/posthog/send_worker.rb
CHANGED
|
@@ -48,6 +48,7 @@ module PostHog
|
|
|
48
48
|
skip_ssl_verification: options[:skip_ssl_verification],
|
|
49
49
|
compress_request: options[:compress_request]
|
|
50
50
|
}
|
|
51
|
+
@transport_options[:retries] = options[:max_retries].to_i + 1 if options.key?(:max_retries)
|
|
51
52
|
@transport = Transport.new(@transport_options)
|
|
52
53
|
end
|
|
53
54
|
|
data/lib/posthog/transport.rb
CHANGED
|
@@ -8,6 +8,7 @@ require 'posthog/backoff_policy'
|
|
|
8
8
|
require 'net/http'
|
|
9
9
|
require 'net/https'
|
|
10
10
|
require 'json'
|
|
11
|
+
require 'time'
|
|
11
12
|
require 'zlib'
|
|
12
13
|
|
|
13
14
|
module PostHog
|
|
@@ -42,11 +43,12 @@ module PostHog
|
|
|
42
43
|
options[:port] = options[:port].nil? ? PORT : options[:port]
|
|
43
44
|
options[:ssl] = options[:ssl].nil? ? SSL : options[:ssl]
|
|
44
45
|
|
|
45
|
-
@headers = options[:headers] || HEADERS
|
|
46
|
+
@headers = (options[:headers] || HEADERS).dup
|
|
46
47
|
@path = options[:path] || PATH
|
|
47
48
|
@retries = options[:retries] || RETRIES
|
|
48
49
|
@backoff_policy = options[:backoff_policy] || PostHog::BackoffPolicy.new
|
|
49
50
|
@compress_request = options[:compress_request] != false
|
|
51
|
+
@last_retry_after = nil
|
|
50
52
|
|
|
51
53
|
http = Net::HTTP.new(options[:host], options[:port])
|
|
52
54
|
http.use_ssl = options[:ssl]
|
|
@@ -103,10 +105,8 @@ module PostHog
|
|
|
103
105
|
private
|
|
104
106
|
|
|
105
107
|
def should_retry_request?(status_code, body)
|
|
106
|
-
if status_code >= 500
|
|
107
|
-
true # Server error
|
|
108
|
-
elsif status_code == 429 # rubocop:disable Lint/DuplicateBranch
|
|
109
|
-
true # Rate limited
|
|
108
|
+
if status_code >= 500 || [408, 429].include?(status_code)
|
|
109
|
+
true # Server error, request timeout, or rate limited
|
|
110
110
|
elsif status_code >= 400
|
|
111
111
|
logger.error(body)
|
|
112
112
|
false # Client error. Do not retry, but log
|
|
@@ -136,15 +136,37 @@ module PostHog
|
|
|
136
136
|
|
|
137
137
|
if should_retry && (retries_remaining > 1)
|
|
138
138
|
logger.debug("Retrying request, #{retries_remaining} retries left")
|
|
139
|
-
sleep(
|
|
139
|
+
sleep(retry_delay_seconds)
|
|
140
140
|
retry_with_backoff(retries_remaining - 1, &block)
|
|
141
141
|
else
|
|
142
142
|
[result, caught_exception]
|
|
143
143
|
end
|
|
144
144
|
end
|
|
145
145
|
|
|
146
|
+
def retry_delay_seconds
|
|
147
|
+
retry_after = parse_retry_after(@last_retry_after)
|
|
148
|
+
@last_retry_after = nil
|
|
149
|
+
return retry_after if retry_after
|
|
150
|
+
|
|
151
|
+
@backoff_policy.next_interval.to_f / 1000
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def parse_retry_after(value)
|
|
155
|
+
return nil if value.nil? || value.empty?
|
|
156
|
+
|
|
157
|
+
seconds = Float(value, exception: false)
|
|
158
|
+
return seconds if seconds && seconds >= 0
|
|
159
|
+
|
|
160
|
+
parsed_time = Time.httpdate(value)
|
|
161
|
+
delay = parsed_time - Time.now
|
|
162
|
+
delay.positive? ? delay : nil
|
|
163
|
+
rescue ArgumentError
|
|
164
|
+
nil
|
|
165
|
+
end
|
|
166
|
+
|
|
146
167
|
# Sends a request for the batch, returns [status_code, body]
|
|
147
168
|
def send_request(api_key, batch)
|
|
169
|
+
@last_retry_after = nil
|
|
148
170
|
payload = JSON.generate(api_key: api_key, batch: batch)
|
|
149
171
|
|
|
150
172
|
request_path, request_headers, request_payload = build_request(@path, @headers, payload)
|
|
@@ -159,6 +181,7 @@ module PostHog
|
|
|
159
181
|
@http_mutex.synchronize do
|
|
160
182
|
@http.start unless @http.started? # Maintain a persistent connection
|
|
161
183
|
response = @http.request(request, request_payload)
|
|
184
|
+
@last_retry_after = response['Retry-After']
|
|
162
185
|
[response.code.to_i, response.body]
|
|
163
186
|
end
|
|
164
187
|
end
|
data/lib/posthog/version.rb
CHANGED