posthog-ruby 3.11.1 → 3.12.1

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: 88289426a920b4e3247b7ed8e3e6c20597b698cdd9c0132c6b7a4f292cdd425b
4
- data.tar.gz: fe6279cc8f3526944564722540b05283a381c1216f59db43179de605bbb46cab
3
+ metadata.gz: f51843606a47a924b1a6b772f02c61c90e9446c16827b3d5b28d92d7fdade73a
4
+ data.tar.gz: 4cfbb395d74bf0f9630b35fafafc6f4eedcdbce9e32a0a8825880f461b581fd9
5
5
  SHA512:
6
- metadata.gz: 398ad49eafef48370ff2d97e817ae35bfb988e0cb1849937a9adb9a1e085e3188cc1c4ae1d1b937d60409aec6ab8bac3ebb70dd9a4c19a86d45911765739f58c
7
- data.tar.gz: 85600802646e6fe88f59ae99e997749f24b9e88ccfd202d1e53ea95f06b1c08d5453313d2838ad4e1edfd90452d27715e89cc646d4237afc8c51449c88cefc5d
6
+ metadata.gz: 9978900c0f4091624f901a72396f4b2915cc4532503d1bd3a17c7f7e27a142463f5dbbe8e40d621cb52ae95871055f720d5a1a7a5e4652f266604a7311e2dd68
7
+ data.tar.gz: a815192b4521d259e0f2b8c3adba4c1ef2ef2b30748b4ca56c38f5b4d2b3633b1d500d7b06fe1f51d7e4046d3df618c82e2f5a0ed84ed12eb2f87d94f57dc7b8
@@ -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,7 +177,7 @@ 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
@@ -745,10 +749,28 @@ module PostHog
745
749
  @feature_flags_poller.load_feature_flags(true)
746
750
  end
747
751
 
752
+ # Whether the client will actually send events. It is disabled when the
753
+ # api_key is missing or blank, in which case every capture call no-ops.
754
+ #
755
+ # @return [Boolean]
756
+ def enabled?
757
+ !@disabled
758
+ end
759
+
748
760
  # Flush pending events and stop background resources.
749
761
  #
750
762
  # @return [void]
751
763
  def shutdown
764
+ already_shutdown = @shutdown_mutex.synchronize do
765
+ if @shutdown
766
+ true
767
+ else
768
+ @shutdown = true
769
+ false
770
+ end
771
+ end
772
+ return if already_shutdown
773
+
752
774
  self.class._decrement_instance_count(@api_key) unless @disabled
753
775
  @feature_flags_poller&.shutdown_poller
754
776
  flush
@@ -756,6 +778,7 @@ module PostHog
756
778
  @sync_lock.synchronize { @transport&.shutdown }
757
779
  else
758
780
  @worker&.shutdown
781
+ @worker_thread&.join(1)
759
782
  end
760
783
  end
761
784
 
@@ -813,7 +836,16 @@ module PostHog
813
836
  ''
814
837
  end
815
838
  reported_key = "#{key}_#{response_repr}#{groups_repr}"
816
- return if @distinct_id_has_sent_flag_calls[distinct_id].include?(reported_key)
839
+ should_capture = @distinct_id_has_sent_flag_calls_mutex.synchronize do
840
+ sent_keys = @distinct_id_has_sent_flag_calls[distinct_id]
841
+ if sent_keys.include?(reported_key)
842
+ false
843
+ else
844
+ sent_keys << reported_key
845
+ true
846
+ end
847
+ end
848
+ return unless should_capture
817
849
 
818
850
  msg = {
819
851
  distinct_id: distinct_id,
@@ -824,7 +856,6 @@ module PostHog
824
856
  msg[:disable_geoip] = disable_geoip unless disable_geoip.nil?
825
857
 
826
858
  capture(msg)
827
- @distinct_id_has_sent_flag_calls[distinct_id] << reported_key
828
859
  end
829
860
 
830
861
  def _feature_flag_evaluations_host
@@ -913,7 +944,7 @@ module PostHog
913
944
  #
914
945
  # returns Boolean of whether the item was added to the queue.
915
946
  def enqueue(action)
916
- return false if @disabled
947
+ return false if @disabled || shutdown?
917
948
 
918
949
  action = process_before_send(action)
919
950
  return false if action.nil? || action.empty?
@@ -926,10 +957,17 @@ module PostHog
926
957
  return true
927
958
  end
928
959
 
929
- if @queue.length < @max_queue_size
930
- @queue << action
931
- ensure_worker_running
960
+ queued = @queue_mutex.synchronize do
961
+ if @queue.length < @max_queue_size
962
+ @queue << action
963
+ true
964
+ else
965
+ false
966
+ end
967
+ end
932
968
 
969
+ if queued
970
+ ensure_worker_running
933
971
  true
934
972
  else
935
973
  logger.warn(
@@ -987,6 +1025,10 @@ module PostHog
987
1025
  @worker_thread&.alive?
988
1026
  end
989
1027
 
1028
+ def shutdown?
1029
+ @shutdown_mutex.synchronize { @shutdown }
1030
+ end
1031
+
990
1032
  def add_local_person_and_group_properties(distinct_id, groups, person_properties, group_properties)
991
1033
  groups ||= {}
992
1034
  person_properties ||= {}
@@ -1250,7 +1250,9 @@ module PostHog
1250
1250
  uri.hostname,
1251
1251
  uri.port,
1252
1252
  use_ssl: uri.scheme == 'https',
1253
- read_timeout: request_timeout
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
@@ -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 Thread.current[:should_exit]
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
- unless @batch.empty?
52
- res = @transport.send @api_key, @batch
53
- @on_error.call(res.status, res.error) unless res.status == 200
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
- @on_error.call(-1, e.to_s)
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
@@ -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
- @http.finish if @http.started?
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
- @http.start unless @http.started? # Maintain a persistent connection
153
- response = @http.request(request, payload)
154
- [response.code.to_i, response.body]
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
@@ -166,5 +166,24 @@ module PostHog
166
166
  super
167
167
  end
168
168
  end
169
+
170
+ # Array that drops the oldest item when it reaches a maximum length.
171
+ #
172
+ # @api private
173
+ class SizeLimitedArray < Array
174
+ # @param max_length [Integer]
175
+ def initialize(max_length)
176
+ super()
177
+ @max_length = max_length
178
+ end
179
+
180
+ # @param value [Object]
181
+ # @return [Array]
182
+ def <<(value)
183
+ shift if length >= @max_length
184
+ super
185
+ end
186
+ end
187
+ private_constant :SizeLimitedArray
169
188
  end
170
189
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PostHog
4
- VERSION = '3.11.1'
4
+ VERSION = '3.12.1'
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.11.1
4
+ version: 3.12.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - ''