tobox 0.7.1 → 0.7.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f199b223858e41b8fae97f8e6d8a07f83b16a349010c4c35d402884fefcf5954
4
- data.tar.gz: f7c5db09b9b477535cceef7522a9afca13bf16da6c4394f426d5a637d290662d
3
+ metadata.gz: 565eb7ae45be10c2048e62cb9b2e3b85787e80bba0b07917ba169016d347274b
4
+ data.tar.gz: 5a235f35084ab94d83d14be26d8ddb75d1a77582998f6bdf39ecf176052f308e
5
5
  SHA512:
6
- metadata.gz: a30f871ceb72033405eab56005aad942e8f34405852d33cf979e9c6a46474ff6e6459d098ed7aacf967140d232035ad1d093b2e27998179606eff7a99b41f8e0
7
- data.tar.gz: 10da8b5d25df57d13d971f37126619872a7ae92208496faf028f2ddd4e66df28e6d76e207498030ee0efd8842231e231b51b1c14f59cdc0b4067873802a4d74b
6
+ metadata.gz: 8548a5fa78246b4d2be197c0f9b2a7d606f4b74c0205622d85e12d54cf649152dcce400668cce0cfc4c2cbcc2ce5ea729e6242f5e918ff989009a2188decc0d9
7
+ data.tar.gz: b377e117f8d1424118efb080dc9df5c22c17c7d2d087d231a52425c4e2166009f2e09fe90e1a5f95aeb58c19120010e77c7225547b0295ebaba2d6f4aa3f1b29
data/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.7.2] - 2026-02-03
4
+
5
+ ### Improvements
6
+
7
+ * Added support for SQLite as a backend.
8
+
9
+ ### Bugfixes
10
+
11
+ * Fixed `sentry` plugin to work with the `sentry-ruby` sdk v6.
12
+ * `:progress` plugin: when using postgresql, the "UPDATE with subquery" used to fetch-and-mark a job to process will use a CTE instead, in order to avoid a known issue with the former where the query planner may decide to scan over the entries of the subquery and cause more UPDATEs than intended when the subquery has a LIMIT.
13
+
3
14
  ## [0.7.1] - 2025-02-07
4
15
 
5
16
  * `:datadog` plugin: support datadog gem v2 or higher.
data/README.md CHANGED
@@ -43,6 +43,8 @@ Simple, data-first events processing framework based on the [transactional outbo
43
43
  * Oracle
44
44
  * Microsoft SQL Server
45
45
 
46
+ `tobox` supports `sqlite` as well, despite its lack of `SKIP LOCKED`, due to the caveat of write operations being performed one-at-a-time.
47
+
46
48
  <a id="markdown-installation" name="installation"></a>
47
49
  ## Installation
48
50
 
@@ -456,6 +458,13 @@ end
456
458
 
457
459
  Timeout (in seconds) after which a previously marked-for-consumption event can be retried (default: `30`)
458
460
 
461
+ ##### `cte_table`
462
+
463
+ the name of the CTE table used in the query to fetch jobs (`:outbox_cte` by default).
464
+
465
+ (This option is only used when the database is PostgreSQL).
466
+
467
+
459
468
  <a id="markdown-event-grouping" name="event-grouping"></a>
460
469
  ### Event grouping
461
470
 
@@ -604,6 +613,8 @@ on_sentry_init do |sentry_cfg|
604
613
  end
605
614
  ```
606
615
 
616
+ This plugin also supports [Sentry transactions](https://docs.sentry.io/product/insights/overview/transaction-summary/), however you're required to be using v6 or higher.
617
+
607
618
  <a id="markdown-datadog" name="datadog"></a>
608
619
  ### Datadog
609
620
 
data/lib/tobox/fetcher.rb CHANGED
@@ -82,9 +82,9 @@ module Tobox
82
82
  end
83
83
 
84
84
  def fetch_event_ids
85
- @pick_next_sql.for_update
86
- .skip_locked
87
- .limit(@batch_size).select(:id) # lock starts here
85
+ ds = @pick_next_sql
86
+ ds = ds.for_update.skip_locked if ds.supports_skip_locked?
87
+ ds.limit(@batch_size).select(:id) # lock starts here
88
88
  end
89
89
 
90
90
  def do_fetch_events
@@ -17,10 +17,9 @@ module Tobox
17
17
  private
18
18
 
19
19
  def fetch_event_ids
20
- group = @pick_next_sql.for_update
21
- .skip_locked
22
- .limit(1)
23
- .select(@group_column)
20
+ group = @pick_next_sql
21
+ group = group.for_update.skip_locked if group.supports_skip_locked?
22
+ group = group.limit(1).select(@group_column)
24
23
 
25
24
  # get total from a group, to compare to the number of future locked rows.
26
25
  total_from_group = @ds.where(@group_column => group).count
@@ -33,7 +32,8 @@ module Tobox
33
32
  event_ids.order(Sequel.desc(@visibility_column, nulls: :first), :id)
34
33
  end
35
34
 
36
- event_ids = event_ids.for_update.skip_locked.select_map(:id)
35
+ event_ids = event_ids.for_update.skip_locked if event_ids.supports_skip_locked?
36
+ event_ids = event_ids.select_map(:id)
37
37
 
38
38
  if event_ids.size != total_from_group
39
39
  # this happens if concurrent workers locked different rows from the same group,
@@ -4,10 +4,17 @@ module Tobox
4
4
  module Plugins
5
5
  module Progress
6
6
  def self.configure(conf)
7
+ conf.config[:cte_table] = :outbox_cte
7
8
  conf.config[:visibility_timeout] = 30
8
9
  end
9
10
 
10
11
  module FetcherMethods
12
+ def initialize(_, configuration)
13
+ super
14
+
15
+ @cte_table = configuration[:cte_table]
16
+ end
17
+
11
18
  private
12
19
 
13
20
  def do_fetch_events
@@ -19,17 +26,26 @@ module Tobox
19
26
  }
20
27
  mark_as_fetched_params[@attempts_column] = Sequel[@table][@attempts_column] + 1 if @attempts_column
21
28
 
22
- mark_as_fetched_params[@visibility_column] = if @configuration.visibility_type_bool?
23
- true
24
- else
25
- Sequel.date_add(
26
- Sequel::CURRENT_TIMESTAMP,
27
- seconds: @configuration[:visibility_timeout]
28
- )
29
- end
29
+ mark_as_fetched_params[@visibility_column] = (@configuration.visibility_type_bool? || Sequel.date_add(
30
+ Sequel::CURRENT_TIMESTAMP,
31
+ seconds: @configuration[:visibility_timeout]
32
+ ))
30
33
 
31
34
  if @ds.supports_returning?(:update)
32
- @ds.where(id: fetch_event_ids).returning.update(mark_as_fetched_params)
35
+ ids = fetch_event_ids
36
+ ids = if @db.database_type == :postgres && @ds.supports_cte?(:update)
37
+ # The Postgres planner can ignore a LIMIT in the subquery, causing more UPDATEs than LIMIT.
38
+ # A know solution or workaround is to use a CTE as an "optimization fence".
39
+ # https://dba.stackexchange.com/questions/69471/postgres-update-limit-1/69497#69497
40
+ ds = @ds.with(@cte_table, ids.select(:id))
41
+ .from(@table, @cte_table)
42
+ .where(Sequel[@cte_table][:id] => Sequel[@table][:id])
43
+ ds = ds.where(Sequel[@table][@attempts_column] < @configuration[:max_attempts]) if @attempts_column
44
+ ds
45
+ else
46
+ @ds.where(id: ids)
47
+ end
48
+ ids.returning.update(mark_as_fetched_params)
33
49
  else
34
50
  event_ids = fetch_event_ids.select_map(:id)
35
51
  events_ds = @ds.where(id: event_ids)
@@ -55,7 +55,7 @@ module Tobox
55
55
 
56
56
  scope.set_transaction_name("#{TOBOX_NAME}/#{event[:type]}") unless scope.transaction_name
57
57
 
58
- transaction = start_transaction(scope.transaction_name, event[:metadata].to_h["sentry_trace"])
58
+ transaction = start_transaction(scope.transaction_name, event[:metadata].to_h)
59
59
 
60
60
  return unless transaction
61
61
 
@@ -94,7 +94,7 @@ module Tobox
94
94
 
95
95
  def start_transaction(transaction_name, sentry_trace)
96
96
  options = { name: transaction_name, op: "tobox" }
97
- transaction = ::Sentry::Transaction.from_sentry_trace(sentry_trace, **options) if sentry_trace
97
+ transaction = ::Sentry.continue_trace(sentry_trace, **options) unless sentry_trace.empty?
98
98
  ::Sentry.start_transaction(transaction: transaction, **options)
99
99
  end
100
100
 
@@ -52,10 +52,16 @@ module Tobox
52
52
 
53
53
  return unless th.alive?
54
54
 
55
- th.raise(KillError)
56
- th.join(grace_shutdown_timeout)
57
- th.kill
58
- th.join(1)
55
+ th.report_on_exception = false
56
+ th.abort_on_exception = false
57
+ begin
58
+ th.raise(KillError)
59
+ th.join(grace_shutdown_timeout)
60
+ th.kill
61
+ th.join(1)
62
+ rescue KillError
63
+ # async re-raises the interrupt error
64
+ end
59
65
  end
60
66
 
61
67
  private
data/lib/tobox/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Tobox
4
- VERSION = "0.7.1"
4
+ VERSION = "0.7.2"
5
5
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tobox
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.1
4
+ version: 0.7.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - HoneyryderChuck
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-02-07 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: logger
@@ -94,7 +94,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
94
94
  - !ruby/object:Gem::Version
95
95
  version: '0'
96
96
  requirements: []
97
- rubygems_version: 3.6.2
97
+ rubygems_version: 3.6.9
98
98
  specification_version: 4
99
99
  summary: Transactional outbox pattern implementation in ruby
100
100
  test_files: []