posthog-ruby 3.12.0 → 3.12.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/client.rb +46 -13
- data/lib/posthog/feature_flags.rb +3 -1
- data/lib/posthog/field_parser.rb +17 -8
- data/lib/posthog/send_worker.rb +25 -7
- data/lib/posthog/transport.rb +9 -4
- data/lib/posthog/utils.rb +19 -12
- 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: 333bb9ea61d04be7324d0c46eb5dbfa2938a583f8fd88bba845ee6300d219702
|
|
4
|
+
data.tar.gz: eeceaa50cc3defed270dc48ad6863e555d3068a21929c0157f03743b943fdde2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a35cbbd21df7355144bf1f1c6f25e36fc1ab3341d09a326941f8d4f41b7e40766019a4935e6930f8874bf18fe9bf4b0a265879c8f6e87f5a4eb930ddac33c923
|
|
7
|
+
data.tar.gz: 9269f70dd837716bcbf638e626a6928dcc32f7f9c3e4e49b53777cac2d4347b050b65d1393c70ff750cdee8a25baad5607467b2da4454d272a0fd1ad6f4b311d
|
data/lib/posthog/client.rb
CHANGED
|
@@ -84,10 +84,13 @@ module PostHog
|
|
|
84
84
|
opts[:host] = normalize_host_option(opts[:host])
|
|
85
85
|
|
|
86
86
|
@queue = Queue.new
|
|
87
|
+
@queue_mutex = Mutex.new
|
|
87
88
|
@api_key = opts[:api_key]
|
|
88
89
|
@disabled = @api_key.nil? || @api_key.empty?
|
|
89
90
|
@max_queue_size = opts[:max_queue_size] || Defaults::Queue::MAX_SIZE
|
|
90
91
|
@worker_mutex = Mutex.new
|
|
92
|
+
@shutdown_mutex = Mutex.new
|
|
93
|
+
@shutdown = false
|
|
91
94
|
@sync_mode = opts[:sync_mode] == true && !opts[:test_mode] && !@disabled
|
|
92
95
|
@on_error = opts[:on_error] || proc { |status, error| }
|
|
93
96
|
@worker = if opts[:test_mode] || @disabled
|
|
@@ -139,8 +142,9 @@ module PostHog
|
|
|
139
142
|
)
|
|
140
143
|
end
|
|
141
144
|
|
|
145
|
+
@distinct_id_has_sent_flag_calls_mutex = Mutex.new
|
|
142
146
|
@distinct_id_has_sent_flag_calls = SizeLimitedHash.new(Defaults::MAX_HASH_SIZE) do |hash, key|
|
|
143
|
-
hash[key] =
|
|
147
|
+
hash[key] = SizeLimitedArray.new(Defaults::MAX_HASH_SIZE)
|
|
144
148
|
end
|
|
145
149
|
|
|
146
150
|
@before_send = opts[:before_send]
|
|
@@ -173,12 +177,14 @@ module PostHog
|
|
|
173
177
|
#
|
|
174
178
|
# @return [void]
|
|
175
179
|
def clear
|
|
176
|
-
@queue.clear
|
|
180
|
+
@queue_mutex.synchronize { @queue.clear }
|
|
177
181
|
end
|
|
178
182
|
|
|
179
183
|
# @!macro common_attrs
|
|
180
|
-
# @option attrs [String] :message_id
|
|
181
|
-
#
|
|
184
|
+
# @option attrs [String] :message_id Deprecated. Use `:uuid` instead. If `:uuid` is absent or
|
|
185
|
+
# invalid and `:message_id` is a valid UUID, it is sent as `uuid` for backwards compatibility.
|
|
186
|
+
# If neither value is valid, the SDK generates a `uuid`. SDK metadata is sent as `$lib` and
|
|
187
|
+
# `$lib_version` properties.
|
|
182
188
|
# @option attrs [Time] :timestamp When the event occurred (optional)
|
|
183
189
|
# @option attrs [String] :distinct_id The ID for this user in your database
|
|
184
190
|
|
|
@@ -757,6 +763,16 @@ module PostHog
|
|
|
757
763
|
#
|
|
758
764
|
# @return [void]
|
|
759
765
|
def shutdown
|
|
766
|
+
already_shutdown = @shutdown_mutex.synchronize do
|
|
767
|
+
if @shutdown
|
|
768
|
+
true
|
|
769
|
+
else
|
|
770
|
+
@shutdown = true
|
|
771
|
+
false
|
|
772
|
+
end
|
|
773
|
+
end
|
|
774
|
+
return if already_shutdown
|
|
775
|
+
|
|
760
776
|
self.class._decrement_instance_count(@api_key) unless @disabled
|
|
761
777
|
@feature_flags_poller&.shutdown_poller
|
|
762
778
|
flush
|
|
@@ -764,6 +780,7 @@ module PostHog
|
|
|
764
780
|
@sync_lock.synchronize { @transport&.shutdown }
|
|
765
781
|
else
|
|
766
782
|
@worker&.shutdown
|
|
783
|
+
@worker_thread&.join(1)
|
|
767
784
|
end
|
|
768
785
|
end
|
|
769
786
|
|
|
@@ -821,7 +838,16 @@ module PostHog
|
|
|
821
838
|
''
|
|
822
839
|
end
|
|
823
840
|
reported_key = "#{key}_#{response_repr}#{groups_repr}"
|
|
824
|
-
|
|
841
|
+
should_capture = @distinct_id_has_sent_flag_calls_mutex.synchronize do
|
|
842
|
+
sent_keys = @distinct_id_has_sent_flag_calls[distinct_id]
|
|
843
|
+
if sent_keys.include?(reported_key)
|
|
844
|
+
false
|
|
845
|
+
else
|
|
846
|
+
sent_keys << reported_key
|
|
847
|
+
true
|
|
848
|
+
end
|
|
849
|
+
end
|
|
850
|
+
return unless should_capture
|
|
825
851
|
|
|
826
852
|
msg = {
|
|
827
853
|
distinct_id: distinct_id,
|
|
@@ -832,7 +858,6 @@ module PostHog
|
|
|
832
858
|
msg[:disable_geoip] = disable_geoip unless disable_geoip.nil?
|
|
833
859
|
|
|
834
860
|
capture(msg)
|
|
835
|
-
@distinct_id_has_sent_flag_calls[distinct_id] << reported_key
|
|
836
861
|
end
|
|
837
862
|
|
|
838
863
|
def _feature_flag_evaluations_host
|
|
@@ -921,23 +946,27 @@ module PostHog
|
|
|
921
946
|
#
|
|
922
947
|
# returns Boolean of whether the item was added to the queue.
|
|
923
948
|
def enqueue(action)
|
|
924
|
-
return false if @disabled
|
|
949
|
+
return false if @disabled || shutdown?
|
|
925
950
|
|
|
926
951
|
action = process_before_send(action)
|
|
927
952
|
return false if action.nil? || action.empty?
|
|
928
953
|
|
|
929
|
-
# add our request id for tracing purposes
|
|
930
|
-
action[:messageId] ||= uid
|
|
931
|
-
|
|
932
954
|
if @sync_mode
|
|
933
955
|
send_sync(action)
|
|
934
956
|
return true
|
|
935
957
|
end
|
|
936
958
|
|
|
937
|
-
|
|
938
|
-
@queue
|
|
939
|
-
|
|
959
|
+
queued = @queue_mutex.synchronize do
|
|
960
|
+
if @queue.length < @max_queue_size
|
|
961
|
+
@queue << action
|
|
962
|
+
true
|
|
963
|
+
else
|
|
964
|
+
false
|
|
965
|
+
end
|
|
966
|
+
end
|
|
940
967
|
|
|
968
|
+
if queued
|
|
969
|
+
ensure_worker_running
|
|
941
970
|
true
|
|
942
971
|
else
|
|
943
972
|
logger.warn(
|
|
@@ -995,6 +1024,10 @@ module PostHog
|
|
|
995
1024
|
@worker_thread&.alive?
|
|
996
1025
|
end
|
|
997
1026
|
|
|
1027
|
+
def shutdown?
|
|
1028
|
+
@shutdown_mutex.synchronize { @shutdown }
|
|
1029
|
+
end
|
|
1030
|
+
|
|
998
1031
|
def add_local_person_and_group_properties(distinct_id, groups, person_properties, group_properties)
|
|
999
1032
|
groups ||= {}
|
|
1000
1033
|
person_properties ||= {}
|
|
@@ -1250,7 +1250,9 @@ module PostHog
|
|
|
1250
1250
|
uri.hostname,
|
|
1251
1251
|
uri.port,
|
|
1252
1252
|
use_ssl: uri.scheme == 'https',
|
|
1253
|
-
|
|
1253
|
+
open_timeout: request_timeout,
|
|
1254
|
+
read_timeout: request_timeout,
|
|
1255
|
+
write_timeout: request_timeout
|
|
1254
1256
|
) do |http|
|
|
1255
1257
|
res = http.request(request_object)
|
|
1256
1258
|
status_code = res.code.to_i
|
data/lib/posthog/field_parser.rb
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'securerandom'
|
|
4
|
+
|
|
3
5
|
require 'posthog/logging'
|
|
4
6
|
|
|
5
7
|
module PostHog
|
|
@@ -25,7 +27,6 @@ module PostHog
|
|
|
25
27
|
event = fields[:event]
|
|
26
28
|
properties = fields[:properties] || {}
|
|
27
29
|
groups = fields[:groups]
|
|
28
|
-
uuid = fields[:uuid]
|
|
29
30
|
check_presence!(event, 'event')
|
|
30
31
|
check_is_hash!(properties, 'properties')
|
|
31
32
|
|
|
@@ -36,8 +37,6 @@ module PostHog
|
|
|
36
37
|
|
|
37
38
|
isoify_dates! properties
|
|
38
39
|
|
|
39
|
-
common['uuid'] = uuid if valid_uuid_for_event_props? uuid
|
|
40
|
-
|
|
41
40
|
common.merge(
|
|
42
41
|
{
|
|
43
42
|
type: 'capture',
|
|
@@ -130,12 +129,13 @@ module PostHog
|
|
|
130
129
|
#
|
|
131
130
|
# - "timestamp"
|
|
132
131
|
# - "distinct_id"
|
|
133
|
-
# - "message_id"
|
|
132
|
+
# - "message_id" (deprecated; normalized to "uuid" when it is a valid UUID)
|
|
134
133
|
# - "send_feature_flags"
|
|
134
|
+
#
|
|
135
|
+
# A new "uuid" is generated when neither "uuid" nor "message_id" is valid.
|
|
135
136
|
def parse_common_fields(fields)
|
|
136
137
|
timestamp = fields[:timestamp] || Time.new
|
|
137
138
|
distinct_id = fields[:distinct_id]
|
|
138
|
-
message_id = fields[:message_id].to_s if fields[:message_id]
|
|
139
139
|
send_feature_flags = fields[:send_feature_flags]
|
|
140
140
|
|
|
141
141
|
check_timestamp! timestamp
|
|
@@ -151,13 +151,12 @@ module PostHog
|
|
|
151
151
|
|
|
152
152
|
parsed = {
|
|
153
153
|
timestamp: datetime_in_iso8601(timestamp),
|
|
154
|
-
library: 'posthog-ruby',
|
|
155
|
-
library_version: PostHog::VERSION.to_s,
|
|
156
|
-
messageId: message_id,
|
|
157
154
|
distinct_id: distinct_id,
|
|
158
155
|
properties: properties
|
|
159
156
|
}
|
|
160
157
|
|
|
158
|
+
parsed['uuid'] = normalized_uuid(fields)
|
|
159
|
+
|
|
161
160
|
if send_feature_flags && fields[:feature_variants]
|
|
162
161
|
feature_variants = fields[:feature_variants]
|
|
163
162
|
active_feature_variants = {}
|
|
@@ -190,6 +189,16 @@ module PostHog
|
|
|
190
189
|
raise ArgumentError, "#{name} must be a Hash" unless obj.is_a? Hash
|
|
191
190
|
end
|
|
192
191
|
|
|
192
|
+
def normalized_uuid(fields)
|
|
193
|
+
uuid = fields[:uuid]
|
|
194
|
+
return uuid if uuid && valid_uuid_for_event_props?(uuid)
|
|
195
|
+
|
|
196
|
+
message_id = fields[:message_id]
|
|
197
|
+
return message_id if message_id && valid_uuid_for_event_props?(message_id)
|
|
198
|
+
|
|
199
|
+
SecureRandom.uuid
|
|
200
|
+
end
|
|
201
|
+
|
|
193
202
|
# @param [Object] uuid - the UUID to validate, user provided, so we don't know the type
|
|
194
203
|
# @return [TrueClass, FalseClass] - true if the UUID is valid or absent, false otherwise
|
|
195
204
|
def valid_uuid_for_event_props?(uuid)
|
data/lib/posthog/send_worker.rb
CHANGED
|
@@ -34,6 +34,8 @@ module PostHog
|
|
|
34
34
|
batch_size = options[:batch_size] || Defaults::MessageBatch::MAX_SIZE
|
|
35
35
|
@batch = MessageBatch.new(batch_size)
|
|
36
36
|
@lock = Mutex.new
|
|
37
|
+
@shutdown_mutex = Mutex.new
|
|
38
|
+
@shutdown = false
|
|
37
39
|
@transport = Transport.new api_host: options[:host], skip_ssl_verification: options[:skip_ssl_verification]
|
|
38
40
|
end
|
|
39
41
|
|
|
@@ -41,26 +43,32 @@ module PostHog
|
|
|
41
43
|
#
|
|
42
44
|
# @return [void]
|
|
43
45
|
def run
|
|
44
|
-
until
|
|
46
|
+
until shutdown?
|
|
45
47
|
return if @queue.empty?
|
|
46
48
|
|
|
47
49
|
@lock.synchronize do
|
|
48
50
|
consume_message_from_queue! until @batch.full? || @queue.empty?
|
|
49
51
|
end
|
|
50
52
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
53
|
+
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
|
|
58
|
+
ensure
|
|
59
|
+
@lock.synchronize { @batch.clear }
|
|
54
60
|
end
|
|
55
|
-
|
|
56
|
-
@lock.synchronize { @batch.clear }
|
|
57
61
|
end
|
|
58
62
|
ensure
|
|
63
|
+
# Worker threads exit when the queue is drained and are restarted for the
|
|
64
|
+
# next burst of events. Close the persistent connection on each exit and
|
|
65
|
+
# let Transport reconnect lazily when a future worker sends another batch.
|
|
59
66
|
@transport.shutdown
|
|
60
67
|
end
|
|
61
68
|
|
|
62
69
|
# @return [void]
|
|
63
70
|
def shutdown
|
|
71
|
+
@shutdown_mutex.synchronize { @shutdown = true }
|
|
64
72
|
@transport.shutdown
|
|
65
73
|
end
|
|
66
74
|
|
|
@@ -74,10 +82,20 @@ module PostHog
|
|
|
74
82
|
|
|
75
83
|
private
|
|
76
84
|
|
|
85
|
+
def shutdown?
|
|
86
|
+
@shutdown_mutex.synchronize { @shutdown }
|
|
87
|
+
end
|
|
88
|
+
|
|
77
89
|
def consume_message_from_queue!
|
|
78
90
|
@batch << @queue.pop
|
|
79
91
|
rescue MessageBatch::JSONGenerationError => e
|
|
80
|
-
|
|
92
|
+
handle_error(-1, e.to_s)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def handle_error(status, error)
|
|
96
|
+
@on_error.call(status, error)
|
|
97
|
+
rescue StandardError => e
|
|
98
|
+
logger.error("Error in on_error callback: #{e.message}")
|
|
81
99
|
end
|
|
82
100
|
end
|
|
83
101
|
end
|
data/lib/posthog/transport.rb
CHANGED
|
@@ -52,6 +52,7 @@ module PostHog
|
|
|
52
52
|
http.verify_mode = OpenSSL::SSL::VERIFY_NONE if options[:skip_ssl_verification]
|
|
53
53
|
|
|
54
54
|
@http = http
|
|
55
|
+
@http_mutex = Mutex.new
|
|
55
56
|
end
|
|
56
57
|
|
|
57
58
|
# Sends a batch of messages to the API
|
|
@@ -91,7 +92,9 @@ module PostHog
|
|
|
91
92
|
#
|
|
92
93
|
# @return [void]
|
|
93
94
|
def shutdown
|
|
94
|
-
@
|
|
95
|
+
@http_mutex.synchronize do
|
|
96
|
+
@http.finish if @http.started?
|
|
97
|
+
end
|
|
95
98
|
end
|
|
96
99
|
|
|
97
100
|
private
|
|
@@ -149,9 +152,11 @@ module PostHog
|
|
|
149
152
|
|
|
150
153
|
[200, '{}']
|
|
151
154
|
else
|
|
152
|
-
@
|
|
153
|
-
|
|
154
|
-
|
|
155
|
+
@http_mutex.synchronize do
|
|
156
|
+
@http.start unless @http.started? # Maintain a persistent connection
|
|
157
|
+
response = @http.request(request, payload)
|
|
158
|
+
[response.code.to_i, response.body]
|
|
159
|
+
end
|
|
155
160
|
end
|
|
156
161
|
end
|
|
157
162
|
|
data/lib/posthog/utils.rb
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'securerandom'
|
|
4
|
-
|
|
5
3
|
module PostHog
|
|
6
4
|
class InconclusiveMatchError < StandardError
|
|
7
5
|
end
|
|
@@ -60,16 +58,6 @@ module PostHog
|
|
|
60
58
|
hash.replace isoify_dates hash
|
|
61
59
|
end
|
|
62
60
|
|
|
63
|
-
# public: Returns a uid string
|
|
64
|
-
#
|
|
65
|
-
def uid
|
|
66
|
-
arr = SecureRandom.random_bytes(16).unpack('NnnnnN')
|
|
67
|
-
arr[2] = (arr[2] & 0x0fff) | 0x4000
|
|
68
|
-
arr[3] = (arr[3] & 0x3fff) | 0x8000
|
|
69
|
-
|
|
70
|
-
'%08x-%04x-%04x-%04x-%04x%08x' % arr # rubocop:disable Style/FormatStringToken, Style/FormatString
|
|
71
|
-
end
|
|
72
|
-
|
|
73
61
|
def datetime_in_iso8601(datetime)
|
|
74
62
|
case datetime
|
|
75
63
|
when Time
|
|
@@ -166,5 +154,24 @@ module PostHog
|
|
|
166
154
|
super
|
|
167
155
|
end
|
|
168
156
|
end
|
|
157
|
+
|
|
158
|
+
# Array that drops the oldest item when it reaches a maximum length.
|
|
159
|
+
#
|
|
160
|
+
# @api private
|
|
161
|
+
class SizeLimitedArray < Array
|
|
162
|
+
# @param max_length [Integer]
|
|
163
|
+
def initialize(max_length)
|
|
164
|
+
super()
|
|
165
|
+
@max_length = max_length
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# @param value [Object]
|
|
169
|
+
# @return [Array]
|
|
170
|
+
def <<(value)
|
|
171
|
+
shift if length >= @max_length
|
|
172
|
+
super
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
private_constant :SizeLimitedArray
|
|
169
176
|
end
|
|
170
177
|
end
|
data/lib/posthog/version.rb
CHANGED