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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ab01f90a9fbe05af027e92831b58e906eb38b30914230fd83a35cab28da40fcd
4
- data.tar.gz: cf06c8facfe324712e096cedfeeef90c755705bcce79384cda2d7b5f4d675cf0
3
+ metadata.gz: 87c4c21f1799cc12f7e4792f728194091a4e3abfe28fa26315469c43cbefd6ba
4
+ data.tar.gz: 4fa4b2455dcf53c3e805616af1e5eb5ae266729f90832f4c4491147c6626f71d
5
5
  SHA512:
6
- metadata.gz: 6825662770b6a96fce094e2c46619fe142144ef8098db49c6a67b4f1faf54d731b65c8767f985413c91bf097767db84648fe2b2ba938ca4f788a0201ffa6002e
7
- data.tar.gz: 8501399ef589da90e3e7c65629df24d99012240c718874a506fede7833225316a1c97881a8eca1df06a831eace6e9090e3b052686c7656c8cd9a3a1815e3a6b7
6
+ metadata.gz: b1fce8fb104ff14b8564649222d729fdd6feda8d3382119eee8c034663108edb972932aaee7d17de6d87911816986e55236a554ce19ba107e032bc04f4fdab7d
7
+ data.tar.gz: b95fecee03f14f695410f76230e8e1284286c4835cd8f4c2ef66779fc57a27bda7e7c3e892a989207ebd85d1f9531a6034a041dade066d776707ae03c7227661
@@ -33,7 +33,7 @@ module PostHog
33
33
 
34
34
  @attempts += 1
35
35
 
36
- [interval, @max_timeout_ms].min
36
+ interval.clamp(@min_timeout_ms, @max_timeout_ms)
37
37
  end
38
38
 
39
39
  private
@@ -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 on a flag
40
- # request. Defaults to {Defaults::FeatureFlags::FLAG_REQUEST_MAX_RETRIES}. Set to 0 to disable retrying.
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] = true if disable_geoip
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(uri, req, @feature_flag_request_timeout_seconds)
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
- begin
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
- retry
1324
+ next
1308
1325
  end
1309
1326
  logger.debug("Unable to complete request to #{_mask_tokens_in_url(uri.to_s)}")
1310
1327
  raise
@@ -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
 
@@ -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(@backoff_policy.next_interval.to_f / 1000)
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PostHog
4
- VERSION = '3.15.0'
4
+ VERSION = '3.15.2'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: posthog-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.15.0
4
+ version: 3.15.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - ''