trifle-stats 2.3.0 → 2.3.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: cace74102291a5e8b9d1de50cc489ea5ca0514e7b23a4a73fb218f9626569f44
4
- data.tar.gz: 7149e5a8928348b7631897abb94c33beca2963b9e1a4d8c1490168e2a2bf4e4b
3
+ metadata.gz: dfb651d581720b40adf3219aab3bf3323b89d3046e624ec0c66bb1b633f6dcca
4
+ data.tar.gz: f6a79c45b2bf41370cdbfec36d068d8fca48be14ce4c24e258480cbe41577d73
5
5
  SHA512:
6
- metadata.gz: 6578def9715b7cdee5c236ea592f482888c0c29304224bc5cdae419df49900613cd115a0c5f3e2e920f04aea02cdbe433ad0742b6ceaf981a3a46ecd5c3c541b
7
- data.tar.gz: 4d257ea260be8adc9d8a1868199cf1ceb983fc240312b13dc63444bf21ff1c0c2493aa2118a80001c3419758536a2d7e7678bb852b2583930f1e0b58f72b17ad
6
+ metadata.gz: 22f6f8b84aa2494f75a573ee47023ba8a6774c6fd65b3a022fb3df58360bf9e527388afc463603946f67e6c37083dd3205eeba950d6c18dadb661cddc8a965c6
7
+ data.tar.gz: dfd898c4beb30d76982c3476b5c3c533f0bf6279feaae86711ff59a7407d7e0ca99a661873f1fcd86a61dc9d59299dd5cd7cef9f3451a23fcda454386d5ec41e
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- trifle-stats (2.3.0)
4
+ trifle-stats (2.3.1)
5
5
  tzinfo (~> 2.0)
6
6
 
7
7
  GEM
@@ -10,7 +10,7 @@ GEM
10
10
  ast (2.4.2)
11
11
  bson (4.12.1)
12
12
  byebug (11.1.3)
13
- concurrent-ruby (1.3.5)
13
+ concurrent-ruby (1.3.6)
14
14
  diff-lcs (1.4.4)
15
15
  dotenv (2.7.6)
16
16
  mini_portile2 (2.8.9)
@@ -105,8 +105,12 @@ module Trifle
105
105
  reset!
106
106
  end
107
107
 
108
- def store(operation, keys, values)
109
- aggregate? ? store_aggregate(operation, keys, values) : store_linear(operation, keys, values)
108
+ def store(operation, keys, values, tracking_key)
109
+ if aggregate?
110
+ store_aggregate(operation, keys, values, tracking_key)
111
+ else
112
+ store_linear(operation, keys, values, tracking_key)
113
+ end
110
114
  @operation_count += 1
111
115
  end
112
116
 
@@ -135,17 +139,17 @@ module Trifle
135
139
  @operation_count = 0
136
140
  end
137
141
 
138
- def store_linear(operation, keys, values)
139
- @actions << { operation: operation, keys: keys, values: duplicate(values), count: 1 }
142
+ def store_linear(operation, keys, values, tracking_key)
143
+ @actions << build_action(operation, keys, values, tracking_key)
140
144
  end
141
145
 
142
- def store_aggregate(operation, keys, values)
143
- signature = signature_for(operation, keys)
146
+ def store_aggregate(operation, keys, values, tracking_key)
147
+ signature = signature_for(operation, keys, tracking_key)
144
148
  if (entry = @actions[signature])
145
149
  entry[:values] = merge_values(operation, entry[:values], values)
146
150
  entry[:count] += 1
147
151
  else
148
- @actions[signature] = { operation: operation, keys: keys, values: duplicate(values), count: 1 }
152
+ @actions[signature] = build_action(operation, keys, values, tracking_key)
149
153
  end
150
154
  end
151
155
 
@@ -172,11 +176,22 @@ module Trifle
172
176
  current
173
177
  end
174
178
 
175
- def signature_for(operation, keys)
179
+ def signature_for(operation, keys, tracking_key)
180
+ tracking_marker = tracking_key || '__tracked__'
176
181
  identifiers = keys.map do |key|
177
182
  [key.prefix, key.key, key.granularity, key.at&.to_i].join(':')
178
183
  end
179
- "#{operation}-#{identifiers.join('|')}"
184
+ "#{operation}-#{tracking_marker}-#{identifiers.join('|')}"
185
+ end
186
+
187
+ def build_action(operation, keys, values, tracking_key)
188
+ {
189
+ operation: operation,
190
+ keys: keys,
191
+ values: duplicate(values),
192
+ count: 1,
193
+ tracking_key: tracking_key
194
+ }
180
195
  end
181
196
 
182
197
  def duplicate(value)
@@ -231,12 +246,12 @@ module Trifle
231
246
  self.class.register(self)
232
247
  end
233
248
 
234
- def inc(keys:, values:)
235
- enqueue(:inc, keys: keys, values: values)
249
+ def inc(keys:, values:, tracking_key: nil)
250
+ enqueue(:inc, keys: keys, values: values, tracking_key: tracking_key)
236
251
  end
237
252
 
238
- def set(keys:, values:)
239
- enqueue(:set, keys: keys, values: values)
253
+ def set(keys:, values:, tracking_key: nil)
254
+ enqueue(:set, keys: keys, values: values, tracking_key: tracking_key)
240
255
  end
241
256
 
242
257
  def flush!
@@ -263,10 +278,10 @@ module Trifle
263
278
 
264
279
  private
265
280
 
266
- def enqueue(operation, keys:, values:)
281
+ def enqueue(operation, keys:, values:, tracking_key:)
267
282
  should_flush = false
268
283
  @mutex.synchronize do
269
- @queue.store(operation, keys, values)
284
+ @queue.store(operation, keys, values, tracking_key)
270
285
  should_flush = @queue.size >= @size
271
286
  end
272
287
 
@@ -336,15 +351,29 @@ module Trifle
336
351
  end
337
352
 
338
353
  def process(actions)
339
- actions.each do |action|
340
- @driver.public_send(
341
- action[:operation], keys: action[:keys], values: action[:values], count: action[:count] || 1
342
- )
343
- end
354
+ actions.each { |action| dispatch_action(action) }
344
355
  ensure
345
356
  release_active_record_connection
346
357
  end
347
358
 
359
+ def dispatch_action(action)
360
+ payload = action_payload(action)
361
+
362
+ if action[:tracking_key]
363
+ @driver.public_send(action[:operation], **payload.merge(tracking_key: action[:tracking_key]))
364
+ else
365
+ @driver.public_send(action[:operation], **payload)
366
+ end
367
+ end
368
+
369
+ def action_payload(action)
370
+ {
371
+ keys: action[:keys],
372
+ values: action[:values],
373
+ count: action[:count] || 1
374
+ }
375
+ end
376
+
348
377
  def start_worker
349
378
  Thread.new do
350
379
  loop do
@@ -50,11 +50,12 @@ module Trifle
50
50
  identifier_for(key)
51
51
  end
52
52
 
53
- def system_data_for(key:, count: 1)
54
- self.class.pack(hash: { data: { count: count, keys: { key.key => count } } })
53
+ def system_data_for(key:, count: 1, tracking_key: nil)
54
+ tracking_key ||= key.key
55
+ self.class.pack(hash: { data: { count: count, keys: { tracking_key => count } } })
55
56
  end
56
57
 
57
- def inc(keys:, values:, count: 1) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
58
+ def inc(keys:, values:, count: 1, tracking_key: nil) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
58
59
  data = self.class.pack(hash: { data: values })
59
60
 
60
61
  if @bulk_write
@@ -63,7 +64,14 @@ module Trifle
63
64
  expire_at = @expire_after ? key.at + @expire_after : nil
64
65
 
65
66
  ops << upsert_operation('$inc', filter: filter, data: data, expire_at: expire_at)
66
- ops << upsert_operation('$inc', filter: system_identifier_for(key: key), data: system_data_for(key: key, count: count), expire_at: expire_at) if @system_tracking # rubocop:disable Layout/LineLength
67
+ next unless @system_tracking
68
+
69
+ ops << upsert_operation(
70
+ '$inc',
71
+ filter: system_identifier_for(key: key),
72
+ data: system_data_for(key: key, count: count, tracking_key: tracking_key),
73
+ expire_at: expire_at
74
+ )
67
75
  end
68
76
 
69
77
  collection.bulk_write(operations)
@@ -74,12 +82,19 @@ module Trifle
74
82
  update = build_update('$inc', data: data, expire_at: expire_at)
75
83
 
76
84
  collection.update_many(filter, update, upsert: true)
77
- collection.update_many(system_identifier_for(key: key), build_update('$inc', data: system_data_for(key: key, count: count), expire_at: expire_at), upsert: true) if @system_tracking # rubocop:disable Layout/LineLength
85
+ next unless @system_tracking
86
+
87
+ system_update = build_update(
88
+ '$inc',
89
+ data: system_data_for(key: key, count: count, tracking_key: tracking_key),
90
+ expire_at: expire_at
91
+ )
92
+ collection.update_many(system_identifier_for(key: key), system_update, upsert: true)
78
93
  end
79
94
  end
80
95
  end
81
96
 
82
- def set(keys:, values:, count: 1) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
97
+ def set(keys:, values:, count: 1, tracking_key: nil) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
83
98
  data = self.class.pack(hash: { data: values })
84
99
 
85
100
  if @bulk_write
@@ -88,7 +103,14 @@ module Trifle
88
103
  expire_at = @expire_after ? key.at + @expire_after : nil
89
104
 
90
105
  ops << upsert_operation('$set', filter: filter, data: data, expire_at: expire_at)
91
- ops << upsert_operation('$inc', filter: system_identifier_for(key: key), data: system_data_for(key: key, count: count), expire_at: expire_at) if @system_tracking # rubocop:disable Layout/LineLength
106
+ next unless @system_tracking
107
+
108
+ ops << upsert_operation(
109
+ '$inc',
110
+ filter: system_identifier_for(key: key),
111
+ data: system_data_for(key: key, count: count, tracking_key: tracking_key),
112
+ expire_at: expire_at
113
+ )
92
114
  end
93
115
 
94
116
  collection.bulk_write(operations)
@@ -99,7 +121,14 @@ module Trifle
99
121
  update = build_update('$set', data: data, expire_at: expire_at)
100
122
 
101
123
  collection.update_many(filter, update, upsert: true)
102
- collection.update_many(system_identifier_for(key: key), build_update('$inc', data: system_data_for(key: key, count: count), expire_at: expire_at), upsert: true) if @system_tracking # rubocop:disable Layout/LineLength
124
+ next unless @system_tracking
125
+
126
+ system_update = build_update(
127
+ '$inc',
128
+ data: system_data_for(key: key, count: count, tracking_key: tracking_key),
129
+ expire_at: expire_at
130
+ )
131
+ collection.update_many(system_identifier_for(key: key), system_update, upsert: true)
103
132
  end
104
133
  end
105
134
  end
@@ -51,17 +51,18 @@ module Trifle
51
51
  identifier_for(key)
52
52
  end
53
53
 
54
- def system_data_for(key:, count: 1)
55
- self.class.pack(hash: { count: count, keys: { key.key => count } })
54
+ def system_data_for(key:, count: 1, tracking_key: nil)
55
+ tracking_key ||= key.key
56
+ self.class.pack(hash: { count: count, keys: { tracking_key => count } })
56
57
  end
57
58
 
58
- def inc(keys:, values:, count: 1)
59
+ def inc(keys:, values:, count: 1, tracking_key: nil)
59
60
  data = self.class.pack(hash: values)
60
61
  client.transaction do |c|
61
62
  keys.map do |key|
62
63
  identifier = identifier_for(key)
63
64
  c.exec(inc_query(identifier: identifier, data: data))
64
- c.exec(inc_query(identifier: system_identifier_for(key: key), data: system_data_for(key: key, count: count))) if @system_tracking # rubocop:disable Layout/LineLength
65
+ track_system_data(c, key, count, tracking_key)
65
66
  end
66
67
  end
67
68
  end
@@ -78,17 +79,26 @@ module Trifle
78
79
  SQL
79
80
  end
80
81
 
81
- def set(keys:, values:, count: 1)
82
+ def set(keys:, values:, count: 1, tracking_key: nil)
82
83
  data = self.class.pack(hash: values)
83
84
  client.transaction do |c|
84
85
  keys.map do |key|
85
86
  identifier = identifier_for(key)
86
87
  c.exec(set_query(identifier: identifier, data: data))
87
- c.exec(inc_query(identifier: system_identifier_for(key: key), data: system_data_for(key: key, count: count))) if @system_tracking # rubocop:disable Layout/LineLength
88
+ track_system_data(c, key, count, tracking_key)
88
89
  end
89
90
  end
90
91
  end
91
92
 
93
+ def track_system_data(connection, key, count, tracking_key)
94
+ return unless @system_tracking
95
+
96
+ system_data = system_data_for(key: key, count: count, tracking_key: tracking_key)
97
+ connection.exec(
98
+ inc_query(identifier: system_identifier_for(key: key), data: system_data)
99
+ )
100
+ end
101
+
92
102
  def set_query(identifier:, data:)
93
103
  columns = identifier.keys.join(', ')
94
104
  values = identifier.values.map { |v| format_value(v) }.join(', ')
@@ -16,7 +16,7 @@ module Trifle
16
16
  "#{self.class.name}(J)"
17
17
  end
18
18
 
19
- def inc(keys:, values:, count: 1) # rubocop:disable Lint/UnusedMethodArgument
19
+ def inc(keys:, values:, count: 1, tracking_key: nil) # rubocop:disable Lint/UnusedMethodArgument
20
20
  keys.map do |key|
21
21
  self.class.pack(hash: values).each do |k, c|
22
22
  d = @data.fetch(key.join(@separator), {})
@@ -26,7 +26,7 @@ module Trifle
26
26
  end
27
27
  end
28
28
 
29
- def set(keys:, values:, count: 1) # rubocop:disable Lint/UnusedMethodArgument
29
+ def set(keys:, values:, count: 1, tracking_key: nil) # rubocop:disable Lint/UnusedMethodArgument
30
30
  keys.map do |key|
31
31
  self.class.pack(hash: values).each do |k, c|
32
32
  d = @data.fetch(key.join(@separator), {})
@@ -26,11 +26,12 @@ module Trifle
26
26
  key.join(separator)
27
27
  end
28
28
 
29
- def system_data_for(key:, count: 1)
30
- self.class.pack(hash: { count: count, keys: { key.key => count } })
29
+ def system_data_for(key:, count: 1, tracking_key: nil)
30
+ tracking_key ||= key.key
31
+ self.class.pack(hash: { count: count, keys: { tracking_key => count } })
31
32
  end
32
33
 
33
- def inc(keys:, values:, count: 1) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
34
+ def inc(keys:, values:, count: 1, tracking_key: nil) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
34
35
  keys.map do |key|
35
36
  key.prefix = prefix
36
37
  pkey = key.join(separator)
@@ -41,13 +42,13 @@ module Trifle
41
42
  next unless @system_tracking
42
43
 
43
44
  skey = system_join_for(key: key)
44
- system_data_for(key: key, count: count).each do |k, c|
45
+ system_data_for(key: key, count: count, tracking_key: tracking_key).each do |k, c|
45
46
  client.hincrby(skey, k, c)
46
47
  end
47
48
  end
48
49
  end
49
50
 
50
- def set(keys:, values:, count: 1)
51
+ def set(keys:, values:, count: 1, tracking_key: nil)
51
52
  keys.map do |key|
52
53
  key.prefix = prefix
53
54
  pkey = key.join(separator)
@@ -56,7 +57,7 @@ module Trifle
56
57
  next unless @system_tracking
57
58
 
58
59
  skey = system_join_for(key: key)
59
- system_data_for(key: key, count: count).each do |k, c|
60
+ system_data_for(key: key, count: count, tracking_key: tracking_key).each do |k, c|
60
61
  client.hincrby(skey, k, c)
61
62
  end
62
63
  end
@@ -53,18 +53,18 @@ module Trifle
53
53
  identifier_for(key)
54
54
  end
55
55
 
56
- def system_data_for(key:, count: 1)
57
- self.class.pack(hash: { count: count, keys: { key.key => count } })
56
+ def system_data_for(key:, count: 1, tracking_key: nil)
57
+ tracking_key ||= key.key
58
+ self.class.pack(hash: { count: count, keys: { tracking_key => count } })
58
59
  end
59
60
 
60
- def inc(keys:, values:, count: 1)
61
+ def inc(keys:, values:, count: 1, tracking_key: nil)
61
62
  data = self.class.pack(hash: values)
62
63
  client.transaction do |c|
63
64
  keys.each do |key|
64
65
  identifier = identifier_for(key)
65
- # Batch data operations to avoid SQLite parser stack overflow
66
66
  batch_data_operations(identifier: identifier, data: data, connection: c, operation: :inc)
67
- batch_data_operations(identifier: system_identifier_for(key: key), data: system_data_for(key: key, count: count), connection: c, operation: :inc) if @system_tracking # rubocop:disable Layout/LineLength
67
+ track_system_data(c, key, count, tracking_key)
68
68
  end
69
69
  end
70
70
  end
@@ -81,18 +81,28 @@ module Trifle
81
81
  SQL
82
82
  end
83
83
 
84
- def set(keys:, values:, count: 1)
84
+ def set(keys:, values:, count: 1, tracking_key: nil)
85
85
  data = self.class.pack(hash: values)
86
86
  client.transaction do |c|
87
87
  keys.each do |key|
88
88
  identifier = identifier_for(key)
89
- # Batch data operations to avoid SQLite parser stack overflow
90
89
  batch_data_operations(identifier: identifier, data: data, connection: c, operation: :set)
91
- batch_data_operations(identifier: system_identifier_for(key: key), data: system_data_for(key: key, count: count), connection: c, operation: :inc) if @system_tracking # rubocop:disable Layout/LineLength
90
+ track_system_data(c, key, count, tracking_key)
92
91
  end
93
92
  end
94
93
  end
95
94
 
95
+ def track_system_data(connection, key, count, tracking_key)
96
+ return unless @system_tracking
97
+
98
+ batch_data_operations(
99
+ identifier: system_identifier_for(key: key),
100
+ data: system_data_for(key: key, count: count, tracking_key: tracking_key),
101
+ connection: connection,
102
+ operation: :inc
103
+ )
104
+ end
105
+
96
106
  def set_query(identifier:, data:)
97
107
  columns = identifier.keys.join(', ')
98
108
  values = identifier.values.map { |v| format_value(v) }.join(', ')
@@ -12,6 +12,7 @@ module Trifle
12
12
  @at = keywords.fetch(:at)
13
13
  @values = keywords.fetch(:values)
14
14
  @config = keywords[:config]
15
+ @untracked = keywords.fetch(:untracked, false)
15
16
  end
16
17
 
17
18
  def config
@@ -25,10 +26,20 @@ module Trifle
25
26
  end
26
27
 
27
28
  def perform
28
- config.storage.inc(
29
+ payload = {
29
30
  keys: config.granularities.map { |granularity| key_for(granularity: granularity) },
30
31
  values: values
31
- )
32
+ }
33
+
34
+ if tracking_key
35
+ config.storage.inc(**payload.merge(tracking_key: tracking_key))
36
+ else
37
+ config.storage.inc(**payload)
38
+ end
39
+ end
40
+
41
+ def tracking_key
42
+ @untracked ? '__untracked__' : nil
32
43
  end
33
44
  end
34
45
  end
@@ -12,6 +12,7 @@ module Trifle
12
12
  @at = keywords.fetch(:at)
13
13
  @values = keywords.fetch(:values)
14
14
  @config = keywords[:config]
15
+ @untracked = keywords.fetch(:untracked, false)
15
16
  end
16
17
 
17
18
  def config
@@ -25,10 +26,20 @@ module Trifle
25
26
  end
26
27
 
27
28
  def perform
28
- config.storage.set(
29
+ payload = {
29
30
  keys: config.granularities.map { |granularity| key_for(granularity: granularity) },
30
31
  values: values
31
- )
32
+ }
33
+
34
+ if tracking_key
35
+ config.storage.set(**payload.merge(tracking_key: tracking_key))
36
+ else
37
+ config.storage.set(**payload)
38
+ end
39
+ end
40
+
41
+ def tracking_key
42
+ @untracked ? '__untracked__' : nil
32
43
  end
33
44
  end
34
45
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Trifle
4
4
  module Stats
5
- VERSION = '2.3.0'
5
+ VERSION = '2.3.1'
6
6
  end
7
7
  end
data/lib/trifle/stats.rb CHANGED
@@ -54,21 +54,23 @@ module Trifle
54
54
  default
55
55
  end
56
56
 
57
- def self.track(key:, at:, values:, config: nil)
57
+ def self.track(key:, at:, values:, config: nil, untracked: false)
58
58
  Trifle::Stats::Operations::Timeseries::Increment.new(
59
59
  key: key,
60
60
  at: at,
61
61
  values: values,
62
- config: config
62
+ config: config,
63
+ untracked: untracked
63
64
  ).perform
64
65
  end
65
66
 
66
- def self.assert(key:, at:, values:, config: nil)
67
+ def self.assert(key:, at:, values:, config: nil, untracked: false)
67
68
  Trifle::Stats::Operations::Timeseries::Set.new(
68
69
  key: key,
69
70
  at: at,
70
71
  values: values,
71
- config: config
72
+ config: config,
73
+ untracked: untracked
72
74
  ).perform
73
75
  end
74
76
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: trifle-stats
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.3.0
4
+ version: 2.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jozef Vaclavik
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-01-23 00:00:00.000000000 Z
11
+ date: 2026-02-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler