posthog-ruby 3.13.1 → 3.14.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/posthog/client.rb +4 -0
- data/lib/posthog/defaults.rb +1 -0
- data/lib/posthog/noop_worker.rb +10 -0
- data/lib/posthog/send_worker.rb +133 -19
- 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: 017e5c02b782e02a2861716768adff003cd203f5996c16e2e8887788f2111fb0
|
|
4
|
+
data.tar.gz: c952a5e44cb1dc847fec2fca8714c886e601b4112151c083508d69f017f6aa4d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2b3af5bf281253d498ec03a219282d1b360337f036911e0eacfeb2cfcc0aaa7b0df0b8d69609191769f10e634937e403ca9af047c889e816ecb4837bd3a1e3e5
|
|
7
|
+
data.tar.gz: 2ccc75bf7e0791aa1e38834c2fa3f1900f9073ed889161caab8cb306d403dcfd4bba44ebb7748d752f9838e311c37506a310210ff62e43617e5eae25f7703e7c
|
data/lib/posthog/client.rb
CHANGED
|
@@ -57,6 +57,8 @@ module PostHog
|
|
|
57
57
|
# @option opts [String] :host Fully qualified hostname of the PostHog server. Defaults to `https://us.i.posthog.com`.
|
|
58
58
|
# @option opts [Integer] :max_queue_size Maximum number of calls to remain queued. Defaults to 10_000.
|
|
59
59
|
# @option opts [Integer] :batch_size Maximum number of events to send in one async batch.
|
|
60
|
+
# @option opts [Numeric] :flush_interval_seconds Maximum seconds to wait for an async batch to fill before sending.
|
|
61
|
+
# Defaults to 5.
|
|
60
62
|
# @option opts [Boolean] :test_mode +true+ if messages should remain queued for testing. Defaults to +false+.
|
|
61
63
|
# @option opts [Boolean] :sync_mode +true+ to send events synchronously on the calling thread. Useful in
|
|
62
64
|
# forking environments like Sidekiq and Resque. Defaults to +false+.
|
|
@@ -172,6 +174,7 @@ module PostHog
|
|
|
172
174
|
|
|
173
175
|
while !@queue.empty? || @worker.is_requesting?
|
|
174
176
|
ensure_worker_running
|
|
177
|
+
@worker.request_flush
|
|
175
178
|
sleep(0.1)
|
|
176
179
|
end
|
|
177
180
|
end
|
|
@@ -972,6 +975,7 @@ module PostHog
|
|
|
972
975
|
|
|
973
976
|
if queued
|
|
974
977
|
ensure_worker_running
|
|
978
|
+
@worker.notify
|
|
975
979
|
true
|
|
976
980
|
else
|
|
977
981
|
logger.warn(
|
data/lib/posthog/defaults.rb
CHANGED
data/lib/posthog/noop_worker.rb
CHANGED
data/lib/posthog/send_worker.rb
CHANGED
|
@@ -23,6 +23,7 @@ module PostHog
|
|
|
23
23
|
# @param api_key [String] Project API key.
|
|
24
24
|
# @param options [Hash] Worker options.
|
|
25
25
|
# @option options [Integer] :batch_size How many items to send in a batch.
|
|
26
|
+
# @option options [Numeric] :flush_interval_seconds Maximum seconds to wait for a batch to fill before sending.
|
|
26
27
|
# @option options [Proc] :on_error Callback invoked as `on_error.call(status, error)`.
|
|
27
28
|
# @option options [String] :host PostHog API host URL.
|
|
28
29
|
# @option options [Boolean] :skip_ssl_verification Disable SSL certificate verification.
|
|
@@ -32,44 +33,74 @@ module PostHog
|
|
|
32
33
|
@api_key = api_key
|
|
33
34
|
@on_error = options[:on_error] || proc { |status, error| }
|
|
34
35
|
batch_size = options[:batch_size] || Defaults::MessageBatch::MAX_SIZE
|
|
36
|
+
flush_interval_seconds = options[:flush_interval_seconds] || Defaults::MessageBatch::FLUSH_INTERVAL_SECONDS
|
|
37
|
+
@flush_interval_seconds = flush_interval_seconds.to_f
|
|
35
38
|
@batch = MessageBatch.new(batch_size)
|
|
36
39
|
@lock = Mutex.new
|
|
37
|
-
@
|
|
40
|
+
@state_lock = Mutex.new
|
|
41
|
+
@condition = ConditionVariable.new
|
|
42
|
+
@flush_requested = false
|
|
38
43
|
@shutdown = false
|
|
39
|
-
@
|
|
44
|
+
@pid = Process.pid
|
|
45
|
+
@transport_options = { api_host: options[:host], skip_ssl_verification: options[:skip_ssl_verification] }
|
|
46
|
+
@transport = Transport.new(@transport_options)
|
|
40
47
|
end
|
|
41
48
|
|
|
42
49
|
# Continuously runs the loop to check for new events.
|
|
43
50
|
#
|
|
44
51
|
# @return [void]
|
|
45
52
|
def run
|
|
53
|
+
ensure_current_process!
|
|
54
|
+
|
|
46
55
|
until shutdown?
|
|
47
|
-
|
|
56
|
+
wait_for_work
|
|
57
|
+
break if shutdown?
|
|
58
|
+
next if @queue.empty?
|
|
48
59
|
|
|
49
|
-
|
|
50
|
-
consume_message_from_queue! until @batch.full? || @queue.empty?
|
|
51
|
-
end
|
|
60
|
+
build_batch
|
|
52
61
|
|
|
53
62
|
begin
|
|
54
|
-
unless @batch.empty?
|
|
55
|
-
res = @transport.send @api_key, @batch
|
|
56
|
-
handle_error(res.status, res.error) unless res.status == 200
|
|
57
|
-
end
|
|
63
|
+
send_batch unless @batch.empty?
|
|
58
64
|
ensure
|
|
59
65
|
@lock.synchronize { @batch.clear }
|
|
66
|
+
clear_flush_request_if_idle
|
|
60
67
|
end
|
|
61
68
|
end
|
|
62
69
|
ensure
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
70
|
+
shutdown_transport
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Request the worker to send any pending events without waiting for the
|
|
74
|
+
# configured flush interval. Used by Client#flush and shutdown paths.
|
|
75
|
+
#
|
|
76
|
+
# @return [void]
|
|
77
|
+
def request_flush
|
|
78
|
+
ensure_current_process!
|
|
79
|
+
|
|
80
|
+
@state_lock.synchronize do
|
|
81
|
+
@flush_requested = true
|
|
82
|
+
@condition.broadcast
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Wake the worker when producers enqueue new messages.
|
|
87
|
+
#
|
|
88
|
+
# @return [void]
|
|
89
|
+
def notify
|
|
90
|
+
ensure_current_process!
|
|
91
|
+
|
|
92
|
+
@state_lock.synchronize { @condition.signal }
|
|
67
93
|
end
|
|
68
94
|
|
|
69
95
|
# @return [void]
|
|
70
96
|
def shutdown
|
|
71
|
-
|
|
72
|
-
|
|
97
|
+
ensure_current_process!
|
|
98
|
+
|
|
99
|
+
@state_lock.synchronize do
|
|
100
|
+
@shutdown = true
|
|
101
|
+
@flush_requested = true
|
|
102
|
+
@condition.broadcast
|
|
103
|
+
end
|
|
73
104
|
end
|
|
74
105
|
|
|
75
106
|
# public: Check whether we have outstanding requests.
|
|
@@ -77,25 +108,108 @@ module PostHog
|
|
|
77
108
|
# @return [Boolean] Whether the worker has outstanding requests.
|
|
78
109
|
# TODO: Rename to `requesting?` in future version
|
|
79
110
|
def is_requesting? # rubocop:disable Naming/PredicateName
|
|
111
|
+
ensure_current_process!
|
|
112
|
+
|
|
80
113
|
@lock.synchronize { !@batch.empty? }
|
|
81
114
|
end
|
|
82
115
|
|
|
83
116
|
private
|
|
84
117
|
|
|
85
|
-
def
|
|
86
|
-
|
|
118
|
+
def ensure_current_process!
|
|
119
|
+
return if @pid == Process.pid
|
|
120
|
+
|
|
121
|
+
@lock.synchronize { @batch.clear }
|
|
122
|
+
@state_lock.synchronize do
|
|
123
|
+
@pid = Process.pid
|
|
124
|
+
@shutdown = false
|
|
125
|
+
@flush_requested = false
|
|
126
|
+
@condition.broadcast
|
|
127
|
+
end
|
|
128
|
+
shutdown_transport
|
|
129
|
+
@transport = Transport.new(@transport_options)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def build_batch
|
|
133
|
+
deadline = monotonic_time + @flush_interval_seconds
|
|
134
|
+
|
|
135
|
+
loop do
|
|
136
|
+
@lock.synchronize do
|
|
137
|
+
consume_message_from_queue! until @batch.full? || @queue.empty?
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
break if @batch.full? || @batch.empty? || flush_requested?
|
|
141
|
+
|
|
142
|
+
remaining = deadline - monotonic_time
|
|
143
|
+
break unless remaining.positive?
|
|
144
|
+
|
|
145
|
+
wait_for_more_messages(remaining)
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def send_batch
|
|
150
|
+
res = @transport.send @api_key, @batch
|
|
151
|
+
handle_error(res.status, res.error) unless res.status == 200
|
|
87
152
|
end
|
|
88
153
|
|
|
89
154
|
def consume_message_from_queue!
|
|
90
|
-
@batch << @queue.pop
|
|
155
|
+
@batch << @queue.pop(true)
|
|
156
|
+
rescue ThreadError
|
|
157
|
+
# Queue was emptied by another thread between #empty? and #pop.
|
|
91
158
|
rescue MessageBatch::JSONGenerationError => e
|
|
92
159
|
handle_error(-1, e.to_s)
|
|
93
160
|
end
|
|
94
161
|
|
|
162
|
+
def wait_for_work
|
|
163
|
+
@state_lock.synchronize do
|
|
164
|
+
while @queue.empty? && !@shutdown
|
|
165
|
+
clear_flush_request_without_lock
|
|
166
|
+
@condition.wait(@state_lock)
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def wait_for_more_messages(timeout)
|
|
172
|
+
@state_lock.synchronize do
|
|
173
|
+
return if @flush_requested || @shutdown || !@queue.empty?
|
|
174
|
+
|
|
175
|
+
@condition.wait(@state_lock, timeout)
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def flush_requested?
|
|
180
|
+
@state_lock.synchronize { @flush_requested }
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def shutdown?
|
|
184
|
+
@state_lock.synchronize { @shutdown }
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def clear_flush_request
|
|
188
|
+
@state_lock.synchronize { clear_flush_request_without_lock }
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def clear_flush_request_if_idle
|
|
192
|
+
@state_lock.synchronize { clear_flush_request_without_lock if @queue.empty? }
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def clear_flush_request_without_lock
|
|
196
|
+
@flush_requested = false
|
|
197
|
+
end
|
|
198
|
+
|
|
95
199
|
def handle_error(status, error)
|
|
96
200
|
@on_error.call(status, error)
|
|
97
201
|
rescue StandardError => e
|
|
98
202
|
logger.error("Error in on_error callback: #{e.message}")
|
|
99
203
|
end
|
|
204
|
+
|
|
205
|
+
def shutdown_transport
|
|
206
|
+
@transport.shutdown
|
|
207
|
+
rescue StandardError => e
|
|
208
|
+
logger.error("Error shutting down transport: #{e.message}")
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def monotonic_time
|
|
212
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
213
|
+
end
|
|
100
214
|
end
|
|
101
215
|
end
|
data/lib/posthog/version.rb
CHANGED