wal 0.0.34 → 0.0.36

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: f7a8f82addc9ffce03d2f1ee6a77bac1f3ae2074a5b427a8c60ca087df2aded6
4
- data.tar.gz: 75559dccba1edf1d0f1a60ebf866726705fe48166d697a1859ec6da644e963f3
3
+ metadata.gz: 87f882233ec5ff20290d81d462f21298948f55769af90df9c402a7ba5cc7c97d
4
+ data.tar.gz: 575345ed49b77a79ed63ae7a2adcfcd21f1662dda4349bebd66863456b642899
5
5
  SHA512:
6
- metadata.gz: 0d0d206e647386006b07e1a89e1a5596346a50894d0260d5def25876902abcbd398c07889a03ac43c88041343e576b2793eec8a1eecdcf47ff1f025587721a38
7
- data.tar.gz: 62b2dbbeadf2c8076f2dee4ad1a95491552dfb0be3af646b03ffa449dd54009cfd940020fe4dfe3b69c2c5e7a707bdafdc2959baebe109ba28d288b31a086031
6
+ metadata.gz: 9b9c6e581d9594eb14a8747c67a5d7cf2dfaae90693524f1bc6e32abeedce15bce53cb15911c5ffb06a84399d7d9d9fb3a3c01039a2367aa16b759dd6ffe596e
7
+ data.tar.gz: bef2b751b85119037d4a0eb46aca01841b6dc45c168da4ff74e7bb4b2b39bba682301cf41e4856e9ee845a25909da8709c943dd037d74e29c967e1e97c9dff18
data/flake.lock ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "nodes": {
3
+ "nixpkgs": {
4
+ "locked": {
5
+ "lastModified": 1792506237,
6
+ "narHash": "sha256-Q/8KLB6jNfpaUN1HAedE/u/Et9feE/zYSsutBLKrCFk=",
7
+ "owner": "NixOS",
8
+ "repo": "nixpkgs",
9
+ "rev": "5adc50831c259f87faeb09ddf930d43597197473",
10
+ "type": "github"
11
+ },
12
+ "original": {
13
+ "owner": "NixOS",
14
+ "ref": "nixpkgs-unstable",
15
+ "repo": "nixpkgs",
16
+ "type": "github"
17
+ }
18
+ },
19
+ "root": {
20
+ "inputs": {
21
+ "nixpkgs": "nixpkgs"
22
+ }
23
+ }
24
+ },
25
+ "root": "root",
26
+ "version": 7
27
+ }
data/flake.nix ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
3
+
4
+ outputs =
5
+ { nixpkgs, ... }:
6
+ let
7
+ forAllSystems = nixpkgs.lib.genAttrs nixpkgs.lib.systems.flakeExposed;
8
+ in
9
+ {
10
+ devShells = forAllSystems (
11
+ system:
12
+ let
13
+ pkgs = nixpkgs.legacyPackages.${system};
14
+ in
15
+ {
16
+ default = pkgs.mkShell {
17
+ packages = with pkgs; [
18
+ ruby_3_4
19
+ libpq
20
+ ];
21
+ };
22
+ }
23
+ );
24
+ };
25
+ }
@@ -22,19 +22,13 @@ module Wal
22
22
  when Wal::BeginTransactionEvent
23
23
  @start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
24
24
  @count = 0
25
- Wal.logger&.debug("[#{@slot}] Begin transaction=#{event.transaction_id} size=#{event.estimated_size}")
26
- when Wal::CommitTransactionEvent
27
- elapsed = ((Process.clock_gettime(Process::CLOCK_MONOTONIC) - @start) * 1000).round(3)
28
- message = "[#{@slot}] Commit transaction=#{event.transaction_id} elapsed=#{elapsed} events=#{@count}"
29
- message << " actions=#{@actions.sort.join(",")}" unless @actions.empty?
30
- message << " tables=#{@tables.sort.join(",")}" unless @tables.empty?
31
- if @count > 1
32
- Wal.logger&.info(message)
33
- else
34
- Wal.logger&.debug(message)
35
- end
36
25
  @actions.clear
37
26
  @tables.clear
27
+ Wal.logger&.debug("[#{@slot}] Begin transaction=#{event.transaction_id} size=#{event.estimated_size}")
28
+ @transaction_open = true
29
+ Wal.hooks[:on_transaction_start]&.call(event, @slot, @watcher)
30
+ when Wal::CommitTransactionEvent
31
+ # Logged after the watcher handles it, so the elapsed time covers its commit processing
38
32
  when Wal::InsertEvent
39
33
  Wal.logger&.debug("[#{@slot}] Insert transaction=#{event.transaction_id} table=#{event.table} primary_key=#{event.primary_key}")
40
34
  @count += 1
@@ -57,7 +51,38 @@ module Wal
57
51
  else
58
52
  @count += 1
59
53
  end
54
+
60
55
  @watcher.on_event(event)
56
+
57
+ if event.is_a?(Wal::CommitTransactionEvent)
58
+ log_commit(event)
59
+ finish_transaction(event)
60
+ end
61
+ rescue Exception
62
+ # The commit will never arrive for this transaction, but the finish hook still has to pair with the start one
63
+ finish_transaction(nil)
64
+ raise
65
+ end
66
+
67
+ private
68
+
69
+ def log_commit(event)
70
+ elapsed = ((Process.clock_gettime(Process::CLOCK_MONOTONIC) - @start) * 1000).round(3)
71
+ message = "[#{@slot}] Commit transaction=#{event.transaction_id} elapsed=#{elapsed} events=#{@count}"
72
+ message << " actions=#{@actions.sort.join(",")}" unless @actions.empty?
73
+ message << " tables=#{@tables.sort.join(",")}" unless @tables.empty?
74
+ if @count > 1
75
+ Wal.logger&.info(message)
76
+ else
77
+ Wal.logger&.debug(message)
78
+ end
79
+ end
80
+
81
+ def finish_transaction(event)
82
+ return unless @transaction_open
83
+
84
+ @transaction_open = false
85
+ Wal.hooks[:on_transaction_finish]&.call(event, @slot, @watcher)
61
86
  end
62
87
  end
63
88
  end
@@ -32,33 +32,45 @@ module Wal
32
32
  def on_event(event)
33
33
  case event
34
34
  when BeginTransactionEvent
35
- @queue = SizedQueue.new(queue_size(event))
36
-
35
+ @event_queue = SizedQueue.new(queue_size(event))
37
36
  event_stream = Enumerator.new do |y|
38
- while (item = @queue.pop)
39
- case item
40
- when CommitTransactionEvent
41
- y << item
42
- break
43
- else
44
- y << item
45
- end
37
+ while (item = @event_queue.pop)
38
+ y << item
39
+ break if item.is_a?(CommitTransactionEvent)
46
40
  end
47
41
  end
48
- @worker = Thread.new { on_transaction_events(event_stream) }
49
-
50
- @queue << event
42
+ ensure_worker
43
+ @transaction_queue << event_stream
44
+ @event_queue << event
51
45
 
52
46
  when CommitTransactionEvent
53
- @queue << event
54
- @worker.join
55
-
56
- # We are cleaning this up to hint to Ruby GC that this can be freed before the next begin transaction arrives
57
- @queue.clear
58
- @queue = nil
47
+ @event_queue << event
48
+ result = @completion_queue.pop
49
+ @event_queue.clear
50
+ raise result if result.is_a? Exception
59
51
 
60
52
  else
61
- @queue << event
53
+ @event_queue << event
54
+ end
55
+ end
56
+
57
+ private
58
+
59
+ def ensure_worker
60
+ return if @worker&.alive?
61
+
62
+ @transaction_queue = Queue.new
63
+ @completion_queue = Queue.new
64
+
65
+ @worker = Thread.new do
66
+ while (event_stream = @transaction_queue.pop)
67
+ begin
68
+ on_transaction_events(event_stream)
69
+ @completion_queue << :done
70
+ rescue Exception => e
71
+ @completion_queue << e
72
+ end
73
+ end
62
74
  end
63
75
  end
64
76
  end
data/lib/wal/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Wal
4
- VERSION = "0.0.34"
4
+ VERSION = "0.0.36"
5
5
  end
data/lib/wal.rb CHANGED
@@ -26,14 +26,29 @@ module Wal
26
26
  @hooks ||= {}
27
27
  end
28
28
 
29
- def on_slot_finished(&block)
30
- hooks[:on_slot_finished] = block
29
+ def on_slot_start(&block)
30
+ hooks[:on_slot_start] = block
31
+ end
32
+
33
+ def on_slot_finish(&block)
34
+ hooks[:on_slot_finish] = block
31
35
  end
32
36
 
33
37
  def on_slot_error(&block)
34
38
  hooks[:on_slot_error] = block
35
39
  end
36
40
 
41
+ # Called before the watcher receives each `BeginTransactionEvent`.
42
+ def on_transaction_start(&block)
43
+ hooks[:on_transaction_start] = block
44
+ end
45
+
46
+ # Called after the watcher handles each `CommitTransactionEvent`, so every bit of the transaction processing runs
47
+ # between the start and finish hooks. When processing raises mid-transaction it is still called, with a `nil` event.
48
+ def on_transaction_finish(&block)
49
+ hooks[:on_transaction_finish] = block
50
+ end
51
+
37
52
  def before_fork(&block)
38
53
  hooks[:before_fork] = block
39
54
  end
data/rbi/wal.rbi CHANGED
@@ -7,7 +7,7 @@ module Wal
7
7
  UpdateEvent,
8
8
  DeleteEvent,
9
9
  ) }
10
- VERSION = "0.0.34"
10
+ VERSION = "0.0.35"
11
11
 
12
12
  class << self
13
13
  sig { returns(T.class_of(Logger)) }
@@ -27,6 +27,12 @@ module Wal
27
27
 
28
28
  sig { params(block: T.proc.params(slot: String, config: T.untyped).void).void }
29
29
  def on_slot_finish(&block); end
30
+
31
+ sig { params(block: T.proc.params(event: Wal::BeginTransactionEvent, slot: String, watcher: Wal::Watcher).void).void }
32
+ def on_transaction_start(&block); end
33
+
34
+ sig { params(block: T.proc.params(event: T.nilable(Wal::CommitTransactionEvent), slot: String, watcher: Wal::Watcher).void).void }
35
+ def on_transaction_finish(&block); end
30
36
  end
31
37
 
32
38
  sig { params(block: T.proc.params(config: T.class_of(Wal)).void).void }
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wal
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.34
4
+ version: 0.0.36
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rodrigo Navarro
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2026-02-11 00:00:00.000000000 Z
10
+ date: 1980-01-01 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: pg
@@ -91,6 +91,8 @@ files:
91
91
  - README.md
92
92
  - Rakefile
93
93
  - exe/wal
94
+ - flake.lock
95
+ - flake.nix
94
96
  - lib/wal.rb
95
97
  - lib/wal/active_record_context_extension.rb
96
98
  - lib/wal/generators/migration.rb
@@ -125,7 +127,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
125
127
  - !ruby/object:Gem::Version
126
128
  version: '0'
127
129
  requirements: []
128
- rubygems_version: 3.6.2
130
+ rubygems_version: 3.7.2
129
131
  specification_version: 4
130
132
  summary: Hook up into Postgres WAL log
131
133
  test_files: []