rudder-sdk-ruby 3.1.1 → 3.2.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 +4 -4
- data/lib/rudder/analytics/backoff_policy.rb +11 -17
- data/lib/rudder/analytics/configuration.rb +34 -2
- data/lib/rudder/analytics/defaults.rb +4 -3
- data/lib/rudder/analytics/field_parser.rb +3 -5
- data/lib/rudder/analytics/retry_policy.rb +117 -0
- data/lib/rudder/analytics/transport.rb +96 -77
- data/lib/rudder/analytics/version.rb +1 -1
- data/lib/rudder/analytics/worker.rb +6 -2
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0c9b8eb6cc946f9d16599987f88eba38e8144ceb857dc546d100c71a060c4d96
|
|
4
|
+
data.tar.gz: a06fe89b9b8a79b6d3a0699efe079056c71f0c2a7220b2c38941685e6856f464
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 61c607dc7ca890d18b76f15caed8f7fb5ca11f6af1e3d11084eec7867c2df4ca2aa9247a64ff429899ac34b0d33d8a5e0be78cfb865a242821efb7a6456f6d66
|
|
7
|
+
data.tar.gz: 8bcde926a31525d8d60e6a3e534fc0806cfcb5db15dfb1da7402ffe4b44ef4bc77ac66ce19500f2200f96387382ca0da59625a38553f58617cc4a61bf4888bd2
|
|
@@ -15,36 +15,30 @@ module Rudder
|
|
|
15
15
|
# @option opts [Numeric] :randomization_factor The randomization factor
|
|
16
16
|
# to use to create a range around the retry interval
|
|
17
17
|
def initialize(opts = {})
|
|
18
|
-
@min_timeout_ms = opts[:min_timeout_ms] || MIN_TIMEOUT_MS
|
|
19
|
-
@max_timeout_ms = opts[:max_timeout_ms] || MAX_TIMEOUT_MS
|
|
20
|
-
@multiplier = opts[:multiplier] || MULTIPLIER
|
|
21
|
-
@randomization_factor = opts[:randomization_factor] || RANDOMIZATION_FACTOR
|
|
18
|
+
@min_timeout_ms = [opts[:min_timeout_ms] || MIN_TIMEOUT_MS, 0].max
|
|
19
|
+
@max_timeout_ms = [opts[:max_timeout_ms] || MAX_TIMEOUT_MS, 0].max
|
|
20
|
+
@multiplier = [opts[:multiplier] || MULTIPLIER, 0].max
|
|
21
|
+
@randomization_factor = [[opts[:randomization_factor] || RANDOMIZATION_FACTOR, 0].max, 1].min
|
|
22
22
|
|
|
23
23
|
@attempts = 0
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
# @return [Numeric] the next backoff interval, in milliseconds.
|
|
27
|
-
def next_interval
|
|
28
|
-
interval = @min_timeout_ms * (@multiplier**@attempts)
|
|
27
|
+
def next_interval(floor_ms = 0)
|
|
28
|
+
interval = [@min_timeout_ms * (@multiplier**@attempts), @max_timeout_ms].min
|
|
29
|
+
interval = [interval, floor_ms].max
|
|
29
30
|
interval = add_jitter(interval, @randomization_factor)
|
|
30
31
|
|
|
31
32
|
@attempts += 1
|
|
32
|
-
|
|
33
|
-
[interval, @max_timeout_ms].min
|
|
33
|
+
interval
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
private
|
|
37
37
|
|
|
38
38
|
def add_jitter(base, randomization_factor)
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
if random_number < 0.5
|
|
44
|
-
base - deviation
|
|
45
|
-
else
|
|
46
|
-
base + deviation
|
|
47
|
-
end
|
|
39
|
+
return base if base <= 0 || randomization_factor <= 0
|
|
40
|
+
|
|
41
|
+
base + (rand * base * randomization_factor)
|
|
48
42
|
end
|
|
49
43
|
end
|
|
50
44
|
end
|
|
@@ -7,7 +7,9 @@ module Rudder
|
|
|
7
7
|
class Configuration
|
|
8
8
|
include Rudder::Analytics::Utils
|
|
9
9
|
|
|
10
|
-
attr_reader :write_key, :data_plane_url, :on_error, :on_error_with_messages, :stub, :gzip, :ssl, :batch_size,
|
|
10
|
+
attr_reader :write_key, :data_plane_url, :on_error, :on_error_with_messages, :stub, :gzip, :ssl, :batch_size,
|
|
11
|
+
:test, :max_queue_size, :backoff_policy, :retries, :retry_base_delay, :max_retry_delay,
|
|
12
|
+
:retry_jitter_ratio, :respect_retry_after
|
|
11
13
|
|
|
12
14
|
def initialize(settings = {})
|
|
13
15
|
symbolized_settings = symbolize_keys(settings)
|
|
@@ -23,12 +25,42 @@ module Rudder
|
|
|
23
25
|
@batch_size = symbolized_settings[:batch_size] || Defaults::MessageBatch::MAX_SIZE
|
|
24
26
|
@gzip = symbolized_settings[:gzip]
|
|
25
27
|
@backoff_policy = symbolized_settings[:backoff_policy]
|
|
26
|
-
|
|
28
|
+
configure_retry(symbolized_settings)
|
|
27
29
|
raise ArgumentError, 'Missing required option :write_key' \
|
|
28
30
|
unless @write_key
|
|
29
31
|
raise ArgumentError, 'Data plane url must be initialized' \
|
|
30
32
|
unless @data_plane_url
|
|
31
33
|
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
def configure_retry(settings)
|
|
38
|
+
@retries = settings[:retries]
|
|
39
|
+
@retry_base_delay = normalize_non_negative_integer(settings[:retry_base_delay])
|
|
40
|
+
@max_retry_delay = normalize_non_negative_integer(
|
|
41
|
+
settings[:max_retry_delay] || settings[:maximum_backoff_duration]
|
|
42
|
+
)
|
|
43
|
+
@retry_jitter_ratio = normalize_jitter_ratio(settings[:retry_jitter_ratio])
|
|
44
|
+
@respect_retry_after = normalize_respect_retry_after(settings)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def normalize_non_negative_integer(value)
|
|
48
|
+
return nil if value.nil?
|
|
49
|
+
|
|
50
|
+
[value.to_i, 0].max
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def normalize_jitter_ratio(value)
|
|
54
|
+
return nil if value.nil?
|
|
55
|
+
|
|
56
|
+
[[value.to_f, 0.0].max, 1.0].min
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def normalize_respect_retry_after(settings)
|
|
60
|
+
return nil unless settings.has_key?(:respect_retry_after)
|
|
61
|
+
|
|
62
|
+
settings[:respect_retry_after] ? true : false
|
|
63
|
+
end
|
|
32
64
|
end
|
|
33
65
|
end
|
|
34
66
|
end
|
|
@@ -11,6 +11,7 @@ module Rudder
|
|
|
11
11
|
'Content-Type' => 'application/json',
|
|
12
12
|
'Content-Encoding' => 'gzip' }
|
|
13
13
|
RETRIES = 10
|
|
14
|
+
MAX_RETRIES = RETRIES - 1
|
|
14
15
|
end
|
|
15
16
|
|
|
16
17
|
module Queue
|
|
@@ -28,9 +29,9 @@ module Rudder
|
|
|
28
29
|
|
|
29
30
|
module BackoffPolicy
|
|
30
31
|
MIN_TIMEOUT_MS = 100
|
|
31
|
-
MAX_TIMEOUT_MS =
|
|
32
|
-
MULTIPLIER =
|
|
33
|
-
RANDOMIZATION_FACTOR = 0.
|
|
32
|
+
MAX_TIMEOUT_MS = 30000
|
|
33
|
+
MULTIPLIER = 2
|
|
34
|
+
RANDOMIZATION_FACTOR = 0.2
|
|
34
35
|
end
|
|
35
36
|
end
|
|
36
37
|
end
|
|
@@ -74,14 +74,12 @@ module Rudder
|
|
|
74
74
|
check_string(group_id, 'group_id')
|
|
75
75
|
|
|
76
76
|
group_data = {
|
|
77
|
-
type
|
|
78
|
-
groupId
|
|
77
|
+
:type => 'group',
|
|
78
|
+
:groupId => group_id
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
# Add traits if present
|
|
82
|
-
if fields[:traits]
|
|
83
|
-
group_data[:traits] = fields[:traits]
|
|
84
|
-
end
|
|
82
|
+
group_data[:traits] = fields[:traits] if fields[:traits]
|
|
85
83
|
|
|
86
84
|
common.merge(group_data)
|
|
87
85
|
end
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rudder/analytics/backoff_policy'
|
|
4
|
+
require 'rudder/analytics/defaults'
|
|
5
|
+
require 'net/http'
|
|
6
|
+
require 'net/https'
|
|
7
|
+
require 'time'
|
|
8
|
+
|
|
9
|
+
module Rudder
|
|
10
|
+
class Analytics
|
|
11
|
+
class RetryPolicy
|
|
12
|
+
include Rudder::Analytics::Defaults::Request
|
|
13
|
+
|
|
14
|
+
RETRYABLE_ERRORS = [
|
|
15
|
+
Timeout::Error,
|
|
16
|
+
EOFError,
|
|
17
|
+
IOError,
|
|
18
|
+
SocketError,
|
|
19
|
+
Errno::ECONNREFUSED,
|
|
20
|
+
Errno::ECONNRESET,
|
|
21
|
+
Errno::EHOSTUNREACH,
|
|
22
|
+
Errno::ENETUNREACH,
|
|
23
|
+
Errno::ETIMEDOUT,
|
|
24
|
+
Net::OpenTimeout,
|
|
25
|
+
Net::ReadTimeout,
|
|
26
|
+
Net::ProtocolError,
|
|
27
|
+
OpenSSL::SSL::SSLError
|
|
28
|
+
].freeze
|
|
29
|
+
|
|
30
|
+
attr_reader :backoff_policy, :max_retries
|
|
31
|
+
|
|
32
|
+
def self.from_config(config)
|
|
33
|
+
backoff_policy = config.backoff_policy || Rudder::Analytics::BackoffPolicy.new(backoff_options(config))
|
|
34
|
+
new(
|
|
35
|
+
:retries => config.retries.nil? ? RETRIES : config.retries,
|
|
36
|
+
:respect_retry_after => config.respect_retry_after.nil? ? true : config.respect_retry_after,
|
|
37
|
+
:backoff_policy => backoff_policy
|
|
38
|
+
)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def self.backoff_options(config)
|
|
42
|
+
{
|
|
43
|
+
:min_timeout_ms => config.retry_base_delay || BackoffPolicy::MIN_TIMEOUT_MS,
|
|
44
|
+
:max_timeout_ms => config.max_retry_delay || BackoffPolicy::MAX_TIMEOUT_MS,
|
|
45
|
+
:multiplier => BackoffPolicy::MULTIPLIER,
|
|
46
|
+
:randomization_factor => config.retry_jitter_ratio || BackoffPolicy::RANDOMIZATION_FACTOR
|
|
47
|
+
}
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def initialize(options = {})
|
|
51
|
+
@max_retries = normalize_max_retries(options)
|
|
52
|
+
@respect_retry_after = options.has_key?(:respect_retry_after) ? options[:respect_retry_after] : true
|
|
53
|
+
@backoff_policy = options[:backoff_policy] || Rudder::Analytics::BackoffPolicy.new
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def max_attempts
|
|
57
|
+
@max_retries + 1
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def retryable_status_code?(status_code)
|
|
61
|
+
status_code.zero? || status_code == 429 || (status_code >= 500 && status_code <= 599)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def retryable_exception?(exception)
|
|
65
|
+
RETRYABLE_ERRORS.any? { |error_class| exception.is_a?(error_class) }
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def retry_delay_in_seconds(headers = {})
|
|
69
|
+
interval = next_backoff_interval(retry_after_delay_in_milliseconds(headers))
|
|
70
|
+
interval.to_f / 1000
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
private
|
|
74
|
+
|
|
75
|
+
def normalize_max_retries(options)
|
|
76
|
+
if options.has_key?(:max_retries)
|
|
77
|
+
[options[:max_retries].to_i, 0].max
|
|
78
|
+
elsif options.has_key?(:retries)
|
|
79
|
+
[options[:retries].to_i - 1, 0].max
|
|
80
|
+
else
|
|
81
|
+
MAX_RETRIES
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def next_backoff_interval(retry_after_delay)
|
|
86
|
+
if @backoff_policy.method(:next_interval).arity.zero?
|
|
87
|
+
[@backoff_policy.next_interval, retry_after_delay].max
|
|
88
|
+
else
|
|
89
|
+
@backoff_policy.next_interval(retry_after_delay)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def retry_after_delay_in_milliseconds(headers)
|
|
94
|
+
return 0 unless @respect_retry_after
|
|
95
|
+
|
|
96
|
+
value = header_value(headers, 'Retry-After')
|
|
97
|
+
return 0 if value.nil?
|
|
98
|
+
|
|
99
|
+
parse_retry_after_delay(value)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def header_value(headers, name)
|
|
103
|
+
headers.find { |header_name, _| header_name.to_s.casecmp(name).zero? }&.last
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def parse_retry_after_delay(value)
|
|
107
|
+
value = value.to_s.strip
|
|
108
|
+
return value.to_i * 1000 if value.match?(/\A\d+\z/)
|
|
109
|
+
|
|
110
|
+
retry_at = Time.httpdate(value)
|
|
111
|
+
[((retry_at - Time.now) * 1000).ceil, 0].max
|
|
112
|
+
rescue ArgumentError
|
|
113
|
+
0
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
@@ -5,6 +5,7 @@ require 'rudder/analytics/utils'
|
|
|
5
5
|
require 'rudder/analytics/response'
|
|
6
6
|
require 'rudder/analytics/logging'
|
|
7
7
|
require 'rudder/analytics/backoff_policy'
|
|
8
|
+
require 'rudder/analytics/retry_policy'
|
|
8
9
|
require 'net/http'
|
|
9
10
|
require 'net/https'
|
|
10
11
|
require 'json'
|
|
@@ -23,8 +24,9 @@ module Rudder
|
|
|
23
24
|
def initialize(config)
|
|
24
25
|
@stub = config.stub || false
|
|
25
26
|
@path = PATH
|
|
26
|
-
@
|
|
27
|
-
@
|
|
27
|
+
@retry_policy = Rudder::Analytics::RetryPolicy.from_config(config)
|
|
28
|
+
@retries = @retry_policy.max_attempts
|
|
29
|
+
@backoff_policy = @retry_policy.backoff_policy
|
|
28
30
|
|
|
29
31
|
uri = URI(config.data_plane_url)
|
|
30
32
|
|
|
@@ -37,88 +39,93 @@ module Rudder
|
|
|
37
39
|
@gzip = config.gzip.nil? ? true : config.gzip
|
|
38
40
|
end
|
|
39
41
|
|
|
40
|
-
# Sends a batch of messages to the API
|
|
41
|
-
#
|
|
42
|
-
# @return [Response] API response
|
|
43
42
|
def send(write_key, batch)
|
|
44
43
|
logger.debug("Sending request for #{batch.length} items")
|
|
45
44
|
|
|
46
|
-
|
|
47
|
-
status_code, body = send_request(write_key, batch)
|
|
48
|
-
error = body
|
|
49
|
-
# rudder server now return 'OK'
|
|
50
|
-
# begin
|
|
51
|
-
# error = JSON.parse(body)['error']
|
|
52
|
-
# rescue StandardError
|
|
53
|
-
# error = JSON.parse(body.to_json)
|
|
54
|
-
# end
|
|
55
|
-
|
|
56
|
-
# puts error
|
|
57
|
-
should_retry = should_retry_request?(status_code, body)
|
|
58
|
-
logger.debug("Response status code: #{status_code}")
|
|
59
|
-
logger.debug("Response error: #{error}") if error
|
|
60
|
-
|
|
61
|
-
[Response.new(status_code, error), should_retry]
|
|
62
|
-
end
|
|
45
|
+
retries = 0
|
|
63
46
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
47
|
+
loop do
|
|
48
|
+
begin
|
|
49
|
+
response, headers = build_response(write_key, batch)
|
|
50
|
+
return response unless should_retry_request?(response.status, response.error)
|
|
51
|
+
return response unless retries_remaining?(retries)
|
|
52
|
+
|
|
53
|
+
retries = retry_request(retries, headers, "status #{response.status}")
|
|
54
|
+
rescue StandardError => e
|
|
55
|
+
return error_response(e) unless retryable_exception?(e)
|
|
56
|
+
return error_response(e) unless retries_remaining?(retries)
|
|
57
|
+
|
|
58
|
+
retries = retry_exception(retries, e)
|
|
59
|
+
end
|
|
70
60
|
end
|
|
71
61
|
end
|
|
72
62
|
|
|
73
|
-
# Closes a persistent connection if it exists
|
|
74
63
|
def shutdown
|
|
75
64
|
@http.finish if @http.started?
|
|
76
65
|
end
|
|
77
66
|
|
|
78
67
|
private
|
|
79
68
|
|
|
69
|
+
def build_response(write_key, batch)
|
|
70
|
+
status_code, body, headers = send_request(write_key, batch)
|
|
71
|
+
error = body
|
|
72
|
+
logger.debug("Response status code: #{status_code}")
|
|
73
|
+
logger.debug("Response error: #{error}") if error
|
|
74
|
+
|
|
75
|
+
[Response.new(status_code, error), headers]
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def retries_remaining?(retries)
|
|
79
|
+
retries < @retry_policy.max_retries
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def retry_request(retries, headers, reason)
|
|
83
|
+
retries += 1
|
|
84
|
+
sleep_before_retry(retries, headers, reason)
|
|
85
|
+
retries
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def retry_exception(retries, exception)
|
|
89
|
+
retries += 1
|
|
90
|
+
reset_connection
|
|
91
|
+
sleep_before_retry(retries, {}, "transport error #{exception.class.name}")
|
|
92
|
+
retries
|
|
93
|
+
end
|
|
94
|
+
|
|
80
95
|
def should_retry_request?(status_code, body)
|
|
81
|
-
if status_code >=
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
true # Rate limited
|
|
85
|
-
elsif status_code >= 400
|
|
86
|
-
logger.error(body)
|
|
87
|
-
false # Client error. Do not retry, but log
|
|
88
|
-
else
|
|
89
|
-
false
|
|
90
|
-
end
|
|
96
|
+
logger.error(body) if status_code >= 400 && !retryable_status_code?(status_code)
|
|
97
|
+
|
|
98
|
+
retryable_status_code?(status_code)
|
|
91
99
|
end
|
|
92
100
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
# an exception is raised. `@backoff_policy` is used to determine the
|
|
97
|
-
# duration to sleep between attempts
|
|
98
|
-
#
|
|
99
|
-
# Returns [last_result, raised_exception]
|
|
100
|
-
def retry_with_backoff(retries_remaining, &block)
|
|
101
|
-
result, caught_exception = nil
|
|
102
|
-
should_retry = false
|
|
103
|
-
|
|
104
|
-
begin
|
|
105
|
-
result, should_retry = yield
|
|
106
|
-
return [result, nil] unless should_retry
|
|
107
|
-
rescue StandardError => e
|
|
108
|
-
should_retry = true
|
|
109
|
-
caught_exception = e
|
|
110
|
-
end
|
|
101
|
+
def retryable_status_code?(status_code)
|
|
102
|
+
@retry_policy.retryable_status_code?(status_code)
|
|
103
|
+
end
|
|
111
104
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
105
|
+
def retryable_exception?(exception)
|
|
106
|
+
@retry_policy.retryable_exception?(exception)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def sleep_before_retry(retry_number, headers, reason)
|
|
110
|
+
delay = @retry_policy.retry_delay_in_seconds(headers)
|
|
111
|
+
remaining = @retry_policy.max_retries - retry_number
|
|
112
|
+
logger.debug("Retrying request after #{reason} in #{delay}s " \
|
|
113
|
+
"(attempt #{retry_number} of #{@retry_policy.max_attempts}, #{remaining} retries left)")
|
|
114
|
+
sleep(delay) if delay.positive?
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def error_response(exception)
|
|
118
|
+
logger.error(exception.message)
|
|
119
|
+
exception.backtrace&.each { |line| logger.error(line) }
|
|
120
|
+
Response.new(-1, exception.to_s)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def reset_connection
|
|
124
|
+
@http.finish if @http.started?
|
|
125
|
+
rescue StandardError
|
|
126
|
+
nil
|
|
119
127
|
end
|
|
120
128
|
|
|
121
|
-
# Sends a request for the batch, returns [status_code, body]
|
|
122
129
|
def send_request(write_key, batch)
|
|
123
130
|
payload = {
|
|
124
131
|
:batch => batch.messages
|
|
@@ -127,27 +134,39 @@ module Rudder
|
|
|
127
134
|
logger.debug "stubbed request to #{@path}: " \
|
|
128
135
|
"write key = #{write_key}, batch = #{JSON.generate(payload)}"
|
|
129
136
|
|
|
130
|
-
[200, '{}']
|
|
137
|
+
[200, '{}', {}]
|
|
131
138
|
else
|
|
132
139
|
|
|
133
|
-
headers =
|
|
134
|
-
|
|
135
|
-
if @gzip
|
|
136
|
-
gzip = Zlib::GzipWriter.new(StringIO.new)
|
|
137
|
-
gzip << payload.to_json
|
|
138
|
-
payload = gzip.close.string
|
|
139
|
-
else
|
|
140
|
-
headers.delete('Content-Encoding')
|
|
141
|
-
payload = JSON.generate(payload)
|
|
142
|
-
end
|
|
140
|
+
payload, headers = encoded_payload(payload)
|
|
143
141
|
|
|
144
142
|
request = Net::HTTP::Post.new(@path, headers)
|
|
145
143
|
request.basic_auth(write_key, nil)
|
|
146
144
|
@http.start unless @http.started? # Maintain a persistent connection
|
|
147
145
|
response = @http.request(request, payload)
|
|
148
|
-
[response.code.to_i, response.body]
|
|
146
|
+
[response.code.to_i, response.body, response_headers(response)]
|
|
149
147
|
end
|
|
150
148
|
end
|
|
149
|
+
|
|
150
|
+
def encoded_payload(payload)
|
|
151
|
+
headers = HEADERS.dup
|
|
152
|
+
|
|
153
|
+
if @gzip
|
|
154
|
+
gzip = Zlib::GzipWriter.new(StringIO.new)
|
|
155
|
+
gzip << payload.to_json
|
|
156
|
+
payload = gzip.close.string
|
|
157
|
+
else
|
|
158
|
+
headers.delete('Content-Encoding')
|
|
159
|
+
payload = JSON.generate(payload)
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
[payload, headers]
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def response_headers(response)
|
|
166
|
+
headers = {}
|
|
167
|
+
response.each_header { |name, value| headers[name] = value }
|
|
168
|
+
headers
|
|
169
|
+
end
|
|
151
170
|
end
|
|
152
171
|
end
|
|
153
172
|
end
|
|
@@ -47,8 +47,8 @@ module Rudder
|
|
|
47
47
|
|
|
48
48
|
# res = Request.new(:data_plane_url => @data_plane_url, :ssl => @ssl).post @write_key, @batch
|
|
49
49
|
res = @transport.send @write_key, @batch
|
|
50
|
-
unless res.status
|
|
51
|
-
@on_error.call(res.status, res.error)
|
|
50
|
+
unless success_status?(res.status)
|
|
51
|
+
@on_error.call(res.status, res.error)
|
|
52
52
|
@on_error_with_messages.call(res.status, res.error, @batch.messages)
|
|
53
53
|
end
|
|
54
54
|
|
|
@@ -66,6 +66,10 @@ module Rudder
|
|
|
66
66
|
|
|
67
67
|
private
|
|
68
68
|
|
|
69
|
+
def success_status?(status)
|
|
70
|
+
status >= 200 && status < 300
|
|
71
|
+
end
|
|
72
|
+
|
|
69
73
|
def consume_message_from_queue!
|
|
70
74
|
@batch << @queue.pop
|
|
71
75
|
rescue MessageBatch::JSONGenerationError => e
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rudder-sdk-ruby
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.
|
|
4
|
+
version: 3.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Rudder
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2026-07-21 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: commander
|
|
@@ -141,6 +141,7 @@ files:
|
|
|
141
141
|
- lib/rudder/analytics/logging.rb
|
|
142
142
|
- lib/rudder/analytics/message_batch.rb
|
|
143
143
|
- lib/rudder/analytics/response.rb
|
|
144
|
+
- lib/rudder/analytics/retry_policy.rb
|
|
144
145
|
- lib/rudder/analytics/test_queue.rb
|
|
145
146
|
- lib/rudder/analytics/transport.rb
|
|
146
147
|
- lib/rudder/analytics/utils.rb
|