wal 0.0.15 → 0.0.18
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/README.md +111 -24
- data/exe/wal +61 -6
- data/lib/wal/generators/migration.rb +1 -1
- data/lib/wal/replicator.rb +1 -0
- data/lib/wal/version.rb +1 -1
- data/lib/wal.rb +8 -0
- data/rbi/wal.rbi +9 -1
- metadata +2 -3
- data/sig/wal.rbs +0 -186
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d279bf26f64494f4f52cff1122ade2a9435cd3942be615a92a49f7a5dd64b5d6
|
4
|
+
data.tar.gz: 6b899eacde8085da1ab80bd9cb63741f9b0a7357c1fc86f7821d52d07fbbe48f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8ad343f0b1a3468cf4707e68fcda76ba9859dbba1243c2dfa90c33eef1d0ea50e2791d57ca824163c4f1c7f231ddfeea0c3919400ecba3836c5314947caab6ad
|
7
|
+
data.tar.gz: d5975b490f804064db2e1fba3dd02821d2877927d9333cbc8aa780a347295dd6816eb35a9468163c0d5f301a66d494504b4c195f79f30deb52f56cf48ef2b75a
|
data/README.md
CHANGED
@@ -1,49 +1,136 @@
|
|
1
1
|
# Wal
|
2
2
|
|
3
|
-
|
3
|
+
Wal is a framework that lets you hook into Postgres WAL events directly from your Rails application.
|
4
4
|
|
5
|
-
|
5
|
+
Unlike using database triggers, Wal allows you to keep your logic in your application code while still reacting to persistence events coming from the database.
|
6
6
|
|
7
|
-
|
7
|
+
Also, unlike ActiveRecord callbacks, these events are guaranteed by Postgres to be 100% consistent, ensuring you never miss one.
|
8
8
|
|
9
|
-
|
9
|
+
# Getting started
|
10
|
+
|
11
|
+
## Installation
|
12
|
+
|
13
|
+
Add `wal` to your application's Gemfile:
|
10
14
|
|
11
15
|
```ruby
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
+
gem "wal"
|
17
|
+
```
|
18
|
+
|
19
|
+
And then:
|
20
|
+
|
21
|
+
```bash
|
22
|
+
$ bundle install
|
23
|
+
```
|
24
|
+
|
25
|
+
## Getting started
|
16
26
|
|
17
|
-
|
18
|
-
|
27
|
+
The core building block in Wal is a `Watcher`. The easiest way to create one is by extending `Wal::RecordWatcher`, which handles most of the boilerplate for you.
|
28
|
+
|
29
|
+
For example, let's create a watcher that denormalizes `Post` and `Category` models into a `DenormalizedPost`.
|
30
|
+
|
31
|
+
Create a new file at `app/watchers/denormalize_post_watcher.rb`:
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
class DenormalizePostWatcher < Wal::RecordWatcher
|
35
|
+
# When a new `Post` is created, we create a new `DenormalizedPost` record
|
36
|
+
on_insert Post do |event|
|
37
|
+
DenormalizedPost.create!(
|
38
|
+
post_id: event.primary_key,
|
39
|
+
title: event.new["title"],
|
40
|
+
body: event.new["body"],
|
41
|
+
category_id: event.new["category_id"],
|
42
|
+
category_name: Category.find_by(id: event.new["category_id"])&.name,
|
43
|
+
)
|
19
44
|
end
|
20
45
|
|
21
|
-
|
22
|
-
|
46
|
+
# When a `Post` title or body is changed, we update its `DenormalizedPost` record
|
47
|
+
on_update Post, changed: [:title, :body] do |event|
|
48
|
+
DenormalizedPost
|
49
|
+
.where(post_id: event.primary_key)
|
50
|
+
.update_all(
|
51
|
+
title: event.new["title"],
|
52
|
+
body: event.new["body"],
|
53
|
+
)
|
23
54
|
end
|
24
55
|
|
25
|
-
|
26
|
-
|
56
|
+
# When a `Post` category changes, we also update its `DenormalizedPost` record
|
57
|
+
on_update Post, changed: [:category_id] do |event|
|
58
|
+
DenormalizedPost
|
59
|
+
.where(post_id: event.primary_key)
|
60
|
+
.update_all(
|
61
|
+
category_id: event.new["category_id"],
|
62
|
+
category_name: Category.find_by(id: event.new["category_id"])&.name,
|
63
|
+
)
|
27
64
|
end
|
28
65
|
|
29
|
-
|
30
|
-
|
66
|
+
# When a `Category` changes, we update all the `DenormalizedPosts` referencing it
|
67
|
+
on_update Category, changed: [:name] do |event|
|
68
|
+
DenormalizedPost
|
69
|
+
.where(category_id: event.primary_key)
|
70
|
+
.update_all(
|
71
|
+
category_name: event.new["name"],
|
72
|
+
)
|
31
73
|
end
|
32
74
|
|
33
|
-
|
34
|
-
|
75
|
+
# Finally when a `Category` is deleted, we clear all the `DenormalizedPosts` referencing it
|
76
|
+
on_delete Category do |event|
|
77
|
+
DenormalizedPost
|
78
|
+
.where(category_id: event.primary_key)
|
79
|
+
.update_all(
|
80
|
+
category_id: nil,
|
81
|
+
category_name: nil,
|
82
|
+
)
|
35
83
|
end
|
36
84
|
end
|
37
85
|
```
|
38
86
|
|
39
|
-
|
87
|
+
You might wonder: *Why not just use ActiveRecord callbacks for this?*
|
40
88
|
|
41
|
-
|
42
|
-
|
43
|
-
|
89
|
+
And while it is hard to justify that for our simple example, ActiveRecord callbacks are not guaranteed to always run. Depending on the methods you use to perform the changes, they can be skipped.
|
90
|
+
|
91
|
+
Wal ensures every single change is captured. *Even when updates happen directly in the database and bypass Rails entirely*. That's the main reason to use it: when you need 100% consistency.
|
92
|
+
|
93
|
+
Usually one could resort into database triggers when full consistency is required, but running and maintaining application level code on the database tends to be painful. Wal let's you do the same but at the application level.
|
44
94
|
|
45
|
-
|
46
|
-
|
95
|
+
## Configuring the Watcher
|
96
|
+
|
97
|
+
Wal relies on [Postgres logical replication](https://www.postgresql.org/docs/current/logical-replication.html) to stream changes to your watchers.
|
98
|
+
|
99
|
+
First, create a [Postgres publication](https://www.postgresql.org/docs/current/logical-replication-publication.html) for the tables your watcher uses. Wal provides a generator for this:
|
100
|
+
|
101
|
+
```
|
102
|
+
$ rails generate wal:migration DenormalizePostWatcher
|
103
|
+
```
|
104
|
+
|
105
|
+
This will generate a new migration with all the tables that your watcher uses:
|
106
|
+
```ruby
|
107
|
+
class SetDenormalizePostWatcherPublication < ActiveRecord::Migration
|
108
|
+
def change
|
109
|
+
define_publication :denormalize_post_publication do |p|
|
110
|
+
p.table :posts
|
111
|
+
p.table :categories
|
112
|
+
end
|
47
113
|
end
|
48
114
|
end
|
49
115
|
```
|
116
|
+
|
117
|
+
Next, create a `config/wal.yml` configuration file to link the `Watcher` to its publication:
|
118
|
+
|
119
|
+
```yaml
|
120
|
+
slots:
|
121
|
+
denormalize_posts:
|
122
|
+
watcher: DenormalizePostWatcher
|
123
|
+
publications:
|
124
|
+
- denormalize_post_publication
|
125
|
+
```
|
126
|
+
This associates your watcher with the `denormalize_post_publication` and with the `denormalize_posts` [Postgres replication slot](https://www.postgresql.org/docs/9.4/warm-standby.html#STREAMING-REPLICATION-SLOTS).
|
127
|
+
|
128
|
+
## Running the Watcher
|
129
|
+
|
130
|
+
With everything configured, start the Wal process:
|
131
|
+
|
132
|
+
```bash
|
133
|
+
bundle exec wal start config/wal.yaml
|
134
|
+
```
|
135
|
+
|
136
|
+
Wal will now process your replication slot and run the `DenormalizePostWatcher` whenever a change occur.
|
data/exe/wal
CHANGED
@@ -28,6 +28,47 @@ require "./config/environment"
|
|
28
28
|
|
29
29
|
db_config = ActiveRecord::Base.configurations.configs_for(name: "primary").configuration_hash
|
30
30
|
|
31
|
+
class Wal::LoggingReplicator
|
32
|
+
def initialize(slot, replicator)
|
33
|
+
@slot = slot
|
34
|
+
@replicator = replicator
|
35
|
+
end
|
36
|
+
|
37
|
+
def replicate_forever(watcher, publications:)
|
38
|
+
replication = @replicator.replicate(watcher, publications:)
|
39
|
+
count = 0
|
40
|
+
start = Time.now
|
41
|
+
loop do
|
42
|
+
case (event = replication.next)
|
43
|
+
when Wal::BeginTransactionEvent
|
44
|
+
start = Time.now
|
45
|
+
count = 0
|
46
|
+
if event.estimated_size > 0
|
47
|
+
Wal.logger&.info("[#{@slot}] Begin transaction=#{event.transaction_id} size=#{event.estimated_size}")
|
48
|
+
end
|
49
|
+
when Wal::CommitTransactionEvent
|
50
|
+
if count > 0
|
51
|
+
elapsed = ((Time.now - start) * 1000.0).round(1)
|
52
|
+
Wal.logger&.info("[#{@slot}] Commit transaction=#{event.transaction_id} elapsed=#{elapsed} events=#{count}")
|
53
|
+
end
|
54
|
+
when Wal::InsertEvent
|
55
|
+
Wal.logger&.debug("[#{@slot}] Insert transaction=#{event.transaction_id} table=#{event.table} primary_key=#{event.primary_key}")
|
56
|
+
count += 1
|
57
|
+
when Wal::UpdateEvent
|
58
|
+
Wal.logger&.debug("[#{@slot}] Update transaction=#{event.transaction_id} table=#{event.table} primary_key=#{event.primary_key}")
|
59
|
+
count += 1
|
60
|
+
when Wal::DeleteEvent
|
61
|
+
Wal.logger&.debug("[#{@slot}] Delete transaction=#{event.transaction_id} table=#{event.table} primary_key=#{event.primary_key}")
|
62
|
+
count += 1
|
63
|
+
else
|
64
|
+
count += 1
|
65
|
+
end
|
66
|
+
end
|
67
|
+
rescue StopIteration
|
68
|
+
nil
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
31
72
|
if cli["watch"]
|
32
73
|
watcher = cli["--watcher"].constantize.new
|
33
74
|
use_temporary_slot = cli["--tmp-slot"] || false
|
@@ -37,9 +78,9 @@ if cli["watch"]
|
|
37
78
|
replicator = cli["--replicator"].presence&.constantize || Wal::Replicator
|
38
79
|
|
39
80
|
puts "Watcher started for #{replication_slot} slot (#{publications.join(", ")})"
|
40
|
-
replicator
|
41
|
-
|
42
|
-
|
81
|
+
replicator = replicator.new(replication_slot:, use_temporary_slot:, db_config:)
|
82
|
+
replicator = Wal::LoggingReplicator.new(replication_slot, replicator)
|
83
|
+
replicator.replicate_forever(watcher, publications:)
|
43
84
|
puts "Watcher finished for #{replication_slot}"
|
44
85
|
|
45
86
|
elsif cli["start"]
|
@@ -50,16 +91,30 @@ elsif cli["start"]
|
|
50
91
|
temporary = config["temporary"] || false
|
51
92
|
publications = config["publications"] || []
|
52
93
|
replicator = config["replicator"].presence&.constantize || Wal::Replicator
|
94
|
+
retries = config["retries"]&.to_i || 5
|
53
95
|
|
54
96
|
Thread.new(slot, watcher, temporary, publications) do |replication_slot, watcher, use_temporary_slot, publications|
|
55
97
|
replication_slot = "#{replication_slot}_#{SecureRandom.alphanumeric(4)}" if use_temporary_slot
|
56
98
|
puts "Watcher started for #{replication_slot} slot (#{publications.join(", ")})"
|
57
99
|
|
58
|
-
replicator
|
59
|
-
|
60
|
-
|
100
|
+
replicator = replicator.new(replication_slot:, use_temporary_slot:, db_config:)
|
101
|
+
replicator = Wal::LoggingReplicator.new(replication_slot, replicator)
|
102
|
+
|
103
|
+
begin
|
104
|
+
replicator.replicate_forever(watcher, publications:)
|
105
|
+
rescue StandardError => err
|
106
|
+
if retries > 0
|
107
|
+
Wal.logger&.error("[#{replication_slot}] Error #{err}")
|
108
|
+
retries -= 1
|
109
|
+
sleep 2 ** retries
|
110
|
+
retry
|
111
|
+
end
|
112
|
+
raise
|
113
|
+
end
|
61
114
|
|
62
115
|
puts "Watcher finished for #{replication_slot}"
|
116
|
+
|
117
|
+
Process.kill("TERM", Process.pid)
|
63
118
|
end
|
64
119
|
end
|
65
120
|
|
data/lib/wal/replicator.rb
CHANGED
@@ -88,6 +88,7 @@ module Wal
|
|
88
88
|
|
89
89
|
in XLogData(lsn:, data: PG::Replication::PGOutput::Message(prefix: "wal_ping"))
|
90
90
|
watch_conn.standby_status_update(write_lsn: [watch_conn.last_confirmed_lsn, lsn].compact.max)
|
91
|
+
next
|
91
92
|
|
92
93
|
in XLogData(data: PG::Replication::PGOutput::Message(prefix:, content:)) if watcher.valid_context_prefix? prefix
|
93
94
|
begin
|
data/lib/wal/version.rb
CHANGED
data/lib/wal.rb
CHANGED
@@ -12,6 +12,14 @@ require_relative "wal/railtie"
|
|
12
12
|
require_relative "wal/version"
|
13
13
|
|
14
14
|
module Wal
|
15
|
+
class << self
|
16
|
+
attr_accessor :logger
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.configure(&block)
|
20
|
+
yield self
|
21
|
+
end
|
22
|
+
|
15
23
|
class BeginTransactionEvent < Data.define(:transaction_id, :lsn, :final_lsn, :timestamp)
|
16
24
|
def estimated_size
|
17
25
|
final_lsn - lsn
|
data/rbi/wal.rbi
CHANGED
@@ -7,7 +7,15 @@ module Wal
|
|
7
7
|
UpdateEvent,
|
8
8
|
DeleteEvent,
|
9
9
|
) }
|
10
|
-
VERSION = "0.0.
|
10
|
+
VERSION = "0.0.18"
|
11
|
+
|
12
|
+
class << self
|
13
|
+
sig { returns(T.class_of(Logger)) }
|
14
|
+
attr_accessor :logger
|
15
|
+
end
|
16
|
+
|
17
|
+
sig { params(block: T.proc.params(config: T.class_of(Wal)).void).void }
|
18
|
+
def self.configure(&block); end
|
11
19
|
|
12
20
|
class BeginTransactionEvent < T::Struct
|
13
21
|
prop :transaction_id, Integer, immutable: true
|
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.
|
4
|
+
version: 0.0.18
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rodrigo Navarro
|
8
8
|
bindir: exe
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-09-
|
10
|
+
date: 2025-09-19 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: pg
|
@@ -104,7 +104,6 @@ files:
|
|
104
104
|
- lib/wal/version.rb
|
105
105
|
- lib/wal/watcher.rb
|
106
106
|
- rbi/wal.rbi
|
107
|
-
- sig/wal.rbs
|
108
107
|
licenses:
|
109
108
|
- MIT
|
110
109
|
metadata:
|
data/sig/wal.rbs
DELETED
@@ -1,186 +0,0 @@
|
|
1
|
-
# typed: strong
|
2
|
-
module Wal
|
3
|
-
end
|
4
|
-
|
5
|
-
Wal::Event: untyped
|
6
|
-
|
7
|
-
Wal::VERSION: untyped
|
8
|
-
|
9
|
-
class Wal::BeginTransactionEvent < T::Struct
|
10
|
-
def estimated_size: () -> Integer
|
11
|
-
end
|
12
|
-
|
13
|
-
class Wal::CommitTransactionEvent < T::Struct
|
14
|
-
end
|
15
|
-
|
16
|
-
module Wal::ChangeEvent
|
17
|
-
def diff: () -> ::Hash[String, [ untyped, untyped ]]
|
18
|
-
|
19
|
-
def changed_attribute?: (Symbol | String attribute) -> bool
|
20
|
-
|
21
|
-
def attribute: (Symbol | String attribute) -> untyped
|
22
|
-
|
23
|
-
def attribute_changes: (Symbol | String attribute) -> [ untyped, untyped ]?
|
24
|
-
|
25
|
-
def attribute_was: (Symbol | String attribute) -> untyped
|
26
|
-
end
|
27
|
-
|
28
|
-
class Wal::InsertEvent < T::Struct
|
29
|
-
include ::Wal::ChangeEvent
|
30
|
-
|
31
|
-
def diff: () -> ::Hash[String, [ untyped, untyped ]]
|
32
|
-
end
|
33
|
-
|
34
|
-
class Wal::UpdateEvent < T::Struct
|
35
|
-
include ::Wal::ChangeEvent
|
36
|
-
|
37
|
-
def diff: () -> ::Hash[String, [ untyped, untyped ]]
|
38
|
-
end
|
39
|
-
|
40
|
-
class Wal::DeleteEvent < T::Struct
|
41
|
-
include ::Wal::ChangeEvent
|
42
|
-
|
43
|
-
def diff: () -> ::Hash[String, [ untyped, untyped ]]
|
44
|
-
end
|
45
|
-
|
46
|
-
module Wal::ActiveRecordContextExtension
|
47
|
-
def set_wal_watcher_context: (untyped context, ?prefix: untyped prefix) -> untyped
|
48
|
-
end
|
49
|
-
|
50
|
-
class Wal::NoopWatcher
|
51
|
-
include Wal::Watcher
|
52
|
-
|
53
|
-
def on_event: (Event event) -> void
|
54
|
-
end
|
55
|
-
|
56
|
-
class Wal::RecordWatcher
|
57
|
-
include Wal::Watcher
|
58
|
-
|
59
|
-
extend T::Helpers
|
60
|
-
|
61
|
-
def self.inherited: (untyped subclass) -> untyped
|
62
|
-
|
63
|
-
def self.on_insert: (String | singleton(::ActiveRecord::Base) table) { (InsertEvent event) -> void } -> void
|
64
|
-
|
65
|
-
def self.on_update: (String | singleton(::ActiveRecord::Base) table, ?changed: ::Array[String | Symbol]? changed) { (UpdateEvent event) -> void } -> void
|
66
|
-
|
67
|
-
def self.on_save: (String | singleton(::ActiveRecord::Base) table, ?changed: ::Array[String | Symbol]? changed) { (InsertEvent | UpdateEvent event) -> void } -> void
|
68
|
-
|
69
|
-
def self.on_destroy: (String | singleton(::ActiveRecord::Base) table) { (DeleteEvent event) -> void } -> void
|
70
|
-
|
71
|
-
def on_record_changed: (RecordEvent event) -> void
|
72
|
-
|
73
|
-
def should_watch_table?: (String table) -> bool
|
74
|
-
|
75
|
-
def aggregation_strategy: (BeginTransactionEvent event) -> Symbol
|
76
|
-
|
77
|
-
def on_event: (Event event) -> void
|
78
|
-
end
|
79
|
-
|
80
|
-
Wal::Wal::RecordWatcher::RecordEvent: untyped
|
81
|
-
|
82
|
-
class Wal::Wal::RecordWatcher::MemoryRecordWatcher
|
83
|
-
include Wal::Watcher
|
84
|
-
|
85
|
-
include Wal::Watcher::SeparatedEvents
|
86
|
-
|
87
|
-
extend T::Helpers
|
88
|
-
|
89
|
-
def initialize: (untyped watcher) -> void
|
90
|
-
|
91
|
-
def on_begin: (BeginTransactionEvent event) -> void
|
92
|
-
|
93
|
-
def on_commit: (untyped _event) -> untyped
|
94
|
-
|
95
|
-
def on_insert: (InsertEvent event) -> void
|
96
|
-
|
97
|
-
def on_update: (UpdateEvent event) -> void
|
98
|
-
|
99
|
-
def on_delete: (DeleteEvent event) -> void
|
100
|
-
end
|
101
|
-
|
102
|
-
Wal::Wal::RecordWatcher::Wal::Wal::RecordWatcher::MemoryRecordWatcher::RecordsStorage: untyped
|
103
|
-
|
104
|
-
class Wal::Wal::RecordWatcher::TemporaryTableRecordWatcher
|
105
|
-
include Wal::Watcher
|
106
|
-
|
107
|
-
include Wal::Watcher::SeparatedEvents
|
108
|
-
|
109
|
-
extend T::Helpers
|
110
|
-
|
111
|
-
def initialize: (untyped watcher, ?batch_size: untyped batch_size) -> void
|
112
|
-
|
113
|
-
def on_begin: (BeginTransactionEvent event) -> void
|
114
|
-
|
115
|
-
def on_commit: (untyped _event) -> untyped
|
116
|
-
|
117
|
-
def on_insert: (InsertEvent event) -> void
|
118
|
-
|
119
|
-
def on_update: (UpdateEvent event) -> void
|
120
|
-
|
121
|
-
def on_delete: (DeleteEvent event) -> void
|
122
|
-
|
123
|
-
def base_class: () -> singleton(::ActiveRecord::Base)
|
124
|
-
|
125
|
-
def serialize: (untyped event) -> untyped
|
126
|
-
|
127
|
-
def deserialize: (untyped persisted_event) -> untyped
|
128
|
-
end
|
129
|
-
|
130
|
-
class Wal::Replicator
|
131
|
-
include PG::Replication::Protocol
|
132
|
-
|
133
|
-
def initialize: (?replication_slot: String replication_slot, ?use_temporary_slot: bool use_temporary_slot, ?db_config: ::Hash[Symbol, untyped] db_config) -> void
|
134
|
-
|
135
|
-
def replicate_forever: (Watcher watcher, ?publications: ::Array[String] publications) -> void
|
136
|
-
|
137
|
-
def replicate: (Watcher watcher, ?publications: ::Array[String] publications) -> ::Enumerator::Lazy[Event]
|
138
|
-
end
|
139
|
-
|
140
|
-
class Wal::Wal::Replicator::Column < T::Struct
|
141
|
-
def decode: (untyped value) -> untyped
|
142
|
-
end
|
143
|
-
|
144
|
-
class Wal::Wal::Replicator::Table < T::Struct
|
145
|
-
def primary_key: (untyped decoded_row) -> untyped
|
146
|
-
|
147
|
-
def decode_row: (untyped values) -> untyped
|
148
|
-
end
|
149
|
-
|
150
|
-
class Wal::StreamingWatcher
|
151
|
-
include Wal::Watcher
|
152
|
-
|
153
|
-
extend T::Helpers
|
154
|
-
|
155
|
-
def on_transaction_events: (::Enumerator[Event] events) -> void
|
156
|
-
|
157
|
-
def queue_size: (BeginTransactionEvent event) -> Integer
|
158
|
-
|
159
|
-
def on_event: (Event event) -> void
|
160
|
-
end
|
161
|
-
|
162
|
-
module Wal::Watcher
|
163
|
-
include Wal
|
164
|
-
|
165
|
-
extend T::Helpers
|
166
|
-
|
167
|
-
def on_event: (Event event) -> void
|
168
|
-
|
169
|
-
def should_watch_table?: (String table) -> bool
|
170
|
-
|
171
|
-
def valid_context_prefix?: (String prefix) -> bool
|
172
|
-
end
|
173
|
-
|
174
|
-
module Wal::Wal::Watcher::SeparatedEvents
|
175
|
-
def on_event: (Event event) -> void
|
176
|
-
|
177
|
-
def on_begin: (BeginTransactionEvent event) -> void
|
178
|
-
|
179
|
-
def on_insert: (InsertEvent event) -> void
|
180
|
-
|
181
|
-
def on_update: (UpdateEvent event) -> void
|
182
|
-
|
183
|
-
def on_delete: (DeleteEvent event) -> void
|
184
|
-
|
185
|
-
def on_commit: (CommitTransactionEvent event) -> void
|
186
|
-
end
|