nulogy_message_bus_producer 2.0.0 → 3.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +188 -15
- data/Rakefile +6 -2
- data/db/migrate/20201005150212_rename_tenant_id_and_public.rb +6 -0
- data/lib/nulogy_message_bus_producer.rb +47 -19
- data/lib/nulogy_message_bus_producer/{base_public_subscription.rb → base_subscription.rb} +1 -1
- data/lib/nulogy_message_bus_producer/config.rb +6 -0
- data/lib/nulogy_message_bus_producer/repopulate_replication_slots.rb +25 -0
- data/lib/nulogy_message_bus_producer/subscriber_graphql_schema_validator.rb +1 -1
- data/lib/nulogy_message_bus_producer/{public_subscription.rb → subscription.rb} +1 -2
- data/lib/nulogy_message_bus_producer/{public_subscription_event.rb → subscription_event.rb} +1 -1
- data/lib/nulogy_message_bus_producer/subscriptions/no_variables.rb +43 -0
- data/lib/nulogy_message_bus_producer/subscriptions/postgres_transport.rb +85 -0
- data/lib/nulogy_message_bus_producer/subscriptions/risky_subscription_blocker.rb +70 -0
- data/lib/nulogy_message_bus_producer/version.rb +1 -1
- data/lib/tasks/engine/message_bus_producer.rake +11 -0
- data/spec/dummy/config/database.yml +1 -1
- data/spec/dummy/config/puma.rb +2 -2
- data/spec/dummy/db/migrate/20201005164116_create_active_storage_tables.active_storage.rb +5 -0
- data/spec/dummy/db/schema.rb +3 -5
- data/spec/dummy/log/development.log +510 -0
- data/spec/dummy/log/test.log +18126 -0
- data/spec/integration/lib/nulogy_message_bus_producer/repopulate_replication_slots_spec.rb +141 -0
- data/spec/integration/lib/nulogy_message_bus_producer/subscriber_graphql_schema_validator_spec.rb +49 -0
- data/spec/integration/lib/nulogy_message_bus_producer/subscription_spec.rb +61 -0
- data/spec/integration/lib/nulogy_message_bus_producer/subscriptions/no_variables_spec.rb +46 -0
- data/spec/integration/lib/nulogy_message_bus_producer/subscriptions/postgres_transport_spec.rb +135 -0
- data/spec/integration/lib/nulogy_message_bus_producer/subscriptions/risky_subscription_blocker_spec.rb +49 -0
- data/spec/integration_spec_helper.rb +5 -0
- data/spec/spec_helper.rb +0 -40
- data/spec/support/kafka.rb +105 -0
- data/spec/support/kafka_connect.rb +31 -0
- data/spec/support/spec_utils.rb +16 -0
- data/spec/support/sql_helpers.rb +45 -0
- data/spec/support/subscription_helpers.rb +52 -0
- data/spec/support/test_graphql_schema.rb +48 -0
- metadata +89 -38
- data/lib/nulogy_message_bus_producer/postgres_public_subscriptions.rb +0 -117
- data/spec/integration/lib/graphql_api/postgres_public_subscriptions_spec.rb +0 -122
- data/spec/integration/lib/graphql_api/validators/subscriber_graphql_schema_validator_spec.rb +0 -76
- data/spec/unit/lib/graphql_api/models/public_subscription_spec.rb +0 -66
- data/spec/unit_spec_helper.rb +0 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b81166f96e6dc18fa79dcd9a3d5fd56785e929866c9f6c2bf1571c3b7ca3965d
|
4
|
+
data.tar.gz: e0d7c148e751180d9f336146a17cb56b343d9459af5f436a452a4af4649904d3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9aaf43cc6e873323874a31baa272fcb00ac8b4792b9bead305510ea3f52ec236e5654c3bdcbf5630a3af1466530e434e20958d4134cf75df5cbaf8c16d8a4416
|
7
|
+
data.tar.gz: 33e75bf74472c78810a433036a9658736e9f4b6cb8d506092ffb631bd3a9092ed914be9bb5196783e83d95dcfbabb34b8d45cbdb964154121f95f5caa8f75f14
|
data/README.md
CHANGED
@@ -11,7 +11,7 @@ Nulogy's code for producing to to the Message Bus
|
|
11
11
|
|
12
12
|
This engine contains the classes and validations for producing messages to the Message Bus.
|
13
13
|
|
14
|
-
In general, as long as a subscription inherits from `NulogyMessageBusProducer::
|
14
|
+
In general, as long as a subscription inherits from `NulogyMessageBusProducer::BaseSubscription`, everything
|
15
15
|
should be set up.
|
16
16
|
|
17
17
|
Subscriptions will be written to the `NulogyMessageBusProducer.subscriptions_table_name` while events generated from the subscriptions will be written to
|
@@ -19,34 +19,207 @@ Subscriptions will be written to the `NulogyMessageBusProducer.subscriptions_tab
|
|
19
19
|
|
20
20
|
## Configuration
|
21
21
|
|
22
|
-
| Configuration | Default | Description
|
23
|
-
|
24
|
-
|
|
25
|
-
|
|
22
|
+
| Configuration | Default | Description |
|
23
|
+
|--------------------------------|---------------------------------|---------------------------------------------------------------------------------------------|
|
24
|
+
| context_for_subscription | `->(_subscription) { {} }` | A lambda used to inject any GraphQL Context to be used when producing subscription events |
|
25
|
+
| producing_events_fails_with | :raise | How the producer should handle errors when producing subscription events |
|
26
|
+
| subscriptions_table_name | "message_bus_subscriptions" | The name of the table for the Subscription model |
|
27
|
+
| subscription_events_table_name | "message_bus_subscription_events" | The name of the table for the SubscriptionEvents model |
|
26
28
|
|
27
29
|
## Setup
|
28
30
|
|
29
31
|
You should configure the gem with an initializer. A recommended setup:
|
30
32
|
|
31
|
-
```
|
33
|
+
```ruby
|
32
34
|
# Table names are configurable (optional)
|
33
|
-
NulogyMessageBusProducer.subscriptions_table_name = :
|
34
|
-
NulogyMessageBusProducer.subscription_events_table_name = :
|
35
|
+
NulogyMessageBusProducer.subscriptions_table_name = :message_bus_subscriptions
|
36
|
+
NulogyMessageBusProducer.subscription_events_table_name = :message_bus_subscription_events
|
35
37
|
|
36
38
|
# Register known schemas
|
37
|
-
NulogyMessageBusProducer.
|
39
|
+
NulogyMessageBusProducer.configure do |config|
|
40
|
+
config.register_schema(schema: "ExampleDomain::Schema", key: "exampleDomain")
|
41
|
+
|
42
|
+
config.context_for_subscription = lambda do |_subscription|
|
43
|
+
{
|
44
|
+
current_user: Current.user,
|
45
|
+
current_company: Current.company
|
46
|
+
}
|
47
|
+
end
|
48
|
+
|
49
|
+
# Can fail with either :soft_fail or :raise
|
50
|
+
# :raise will cause the application to halt when event generation fails.
|
51
|
+
# :soft_fail will allow the application to continue, even if generating events fails.
|
52
|
+
# Be very careful when deciding on the behaviour.
|
53
|
+
# Using :raise can result in the application being unusable if a subscription cannot be fulfilled.
|
54
|
+
# Using :soft_fail can result in missing messages, and a lack of consistency between applications.
|
55
|
+
config.producing_events_fails_with(:soft_fail) do |subscription_id:, context:, variables:, result:|
|
56
|
+
# Log in Airbrake / Honeybadger
|
57
|
+
ExceptionNotifier.tagged_message(
|
58
|
+
message: "Generating an event failed!",
|
59
|
+
context: {
|
60
|
+
subscription_id: subscription_id,
|
61
|
+
context: context,
|
62
|
+
variables: variables,
|
63
|
+
result: result
|
64
|
+
}
|
65
|
+
)
|
66
|
+
end
|
67
|
+
end
|
38
68
|
|
39
69
|
# It is *strongly* recommended to run this validation
|
40
|
-
# It ensures the query in a subscription is valid
|
70
|
+
# It ensures the query in a subscription is valid against the current schema
|
41
71
|
# If you do not run this, a change to the schema could break the app
|
42
72
|
# Note: ensure to register schemas before running this
|
43
73
|
NulogyMessageBusProducer.validate_existing!
|
44
74
|
```
|
45
75
|
|
76
|
+
Additionally, your GraphQL Schema will need to use `NulogyMessageBusProducer::Subscriptions::PostgresTransport`, `NulogyMessageBusProducer::Subscriptions::RiskySubscriptionBlocker` and expose subscriptions and queries for the objects to be integrated. Below is an example of how to expose `:sku_created` and `:sku_updated` events.
|
77
|
+
```ruby
|
78
|
+
module ExampleDomainPublicApi
|
79
|
+
class Schema < GraphQL::Schema
|
80
|
+
use NulogyMessageBusProducer::Subscriptions::PostgresTransport
|
81
|
+
|
82
|
+
# You must use the Interpreter, as it will be the new default.
|
83
|
+
# See more here: https://graphql-ruby.org/queries/interpreter
|
84
|
+
use GraphQL::Analysis::AST
|
85
|
+
use GraphQL::Execution::Interpreter
|
86
|
+
|
87
|
+
# This prevents subscriptions which contain arguments or expand lists form being registered.
|
88
|
+
# Expanding unbounded lists inside of an event may severely impact the performance of the system.
|
89
|
+
# Arguments are a source of variation that may not be appropriately tested.
|
90
|
+
# For now, these are banned until we have a use-case which would require them.
|
91
|
+
query_analyzer NulogyMessageBusProducer::Subscriptions::RiskySubscriptionBlocker
|
92
|
+
|
93
|
+
# This prevents creating subscriptions with variables.
|
94
|
+
# Since variables are not saved with the query,
|
95
|
+
# the Subscription would always fail when generating events.
|
96
|
+
query_analyzer NulogyMessageBusProducer::Subscriptions::NoVariables
|
97
|
+
|
98
|
+
query Types::QueryType
|
99
|
+
subscription Types::SubscriptionType
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
module ExampleDomainPublicApi
|
104
|
+
module Types
|
105
|
+
class QueryType < BaseObject
|
106
|
+
description "The query root for this schema"
|
107
|
+
|
108
|
+
field :sku, Types::SkuType, null: false do
|
109
|
+
argument :id, NulogyGraphqlApi::Types::UUID, required: true
|
110
|
+
end
|
111
|
+
|
112
|
+
def sku(id:)
|
113
|
+
Sku.find(id)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
module ExampleDomainPublicApi
|
120
|
+
module Types
|
121
|
+
class SubscriptionType < Types::BaseObject
|
122
|
+
extend GraphQL::Subscriptions::SubscriptionRoot
|
123
|
+
|
124
|
+
description "The subscription root for this schema"
|
125
|
+
|
126
|
+
field :sku_created,
|
127
|
+
subscription: Subscriptions::SkuCreated,
|
128
|
+
description: "Event from creating a SKU. Tenancy is company.uuid"
|
129
|
+
|
130
|
+
field :sku_updated,
|
131
|
+
subscription: Subscriptions::SkuUpdated,
|
132
|
+
description: "Event from updating a SKU. Tenancy is company.uuid"
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
module ExampleDomainPublicApi
|
138
|
+
module Subscriptions
|
139
|
+
class SkuCreated < NulogyMessageBusProducer::BaseSubscription
|
140
|
+
field :sku, Types::SkuType, null: false
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
module ExampleDomainPublicApi
|
146
|
+
module Subscriptions
|
147
|
+
class SkuUpdated < NulogyMessageBusProducer::BaseSubscription
|
148
|
+
field :sku, Types::SkuType, null: false
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
module ExampleDomainPublicApi
|
154
|
+
module Types
|
155
|
+
class SkuType < Types::BaseType
|
156
|
+
field :code, GraphQL::Types::String, null: false
|
157
|
+
|
158
|
+
field :description, GraphQL::Types::String, null: true
|
159
|
+
field :setup_time, GraphQL::Types::Float, null: true
|
160
|
+
field :teardown_time, GraphQL::Types::Float, null: true
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
```
|
165
|
+
|
166
|
+
Events would need to be triggered. Ideally, the underlying domain would be emitting domain events and the events would then be routed in the application layer to the GraphQL API. Where the domain is enemic, this could be done soley in the application layer. In either case, it needs to be invoked in the same transaction as the underlying service.
|
167
|
+
```ruby
|
168
|
+
class CreateSku
|
169
|
+
def initialize(company)
|
170
|
+
@company = company
|
171
|
+
end
|
172
|
+
|
173
|
+
def create(attrs)
|
174
|
+
sku = Sku.new(attrs.merge(company: company))
|
175
|
+
|
176
|
+
if sku.save
|
177
|
+
# This needs to be invoked within the same transaction as Sku.save
|
178
|
+
# Otherwise, the sku could be saved without the event being triggered
|
179
|
+
trigger_subscription("skuCreated", sku)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
private
|
184
|
+
|
185
|
+
def trigger_subscription(event, entity)
|
186
|
+
object = {
|
187
|
+
uuid: entity.id,
|
188
|
+
company_uuid: entity.company_id,
|
189
|
+
context: {}
|
190
|
+
}
|
191
|
+
|
192
|
+
NulogyMessageBusProducer.trigger_event(ExampleDomainPublicApi::Schema, event, object)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
```
|
196
|
+
|
197
|
+
## Resending All Events to the Message Bus
|
198
|
+
|
199
|
+
There is a rake task for resending all events that are currently in the producer's event tables to the message bus.
|
200
|
+
It works by locking the table, deleting all of the events and re-inserting them. This ensures that order is preserved
|
201
|
+
for both existing and newly created events. Unfortunately, the lock will halt most activity on the application server.
|
202
|
+
|
203
|
+
```shell script
|
204
|
+
# WARNING: THIS WILL LOCK THE EVENTS TABLE, AND BY EXTENSION ANY DOMAIN LOGIC WHICH EMITS EVENTS.
|
205
|
+
# THIS IS INTENDED TO BE USED FOR DISASTER RECOVERY ONLY.
|
206
|
+
|
207
|
+
rails message_bus_producer:repopulate_replication_slots
|
208
|
+
```
|
209
|
+
|
46
210
|
## Rake Tasks
|
47
211
|
|
48
|
-
| Rake command | Description
|
49
|
-
|
50
|
-
| (default) | RSpec +RuboCop |
|
51
|
-
| `spec` | RSpec
|
52
|
-
| `rubocop` | RuboCop
|
212
|
+
| Rake command | Description |
|
213
|
+
|--------------|-----------------|
|
214
|
+
| (default) | RSpec + RuboCop |
|
215
|
+
| `spec` | RSpec |
|
216
|
+
| `rubocop` | RuboCop |
|
217
|
+
|
218
|
+
# Gem Development Setup
|
219
|
+
|
220
|
+
If you are doing work on this gem you will want to:
|
221
|
+
```shell script
|
222
|
+
docker-compose up
|
223
|
+
rake db:setup
|
224
|
+
rake
|
225
|
+
```
|
data/Rakefile
CHANGED
@@ -8,8 +8,6 @@ end
|
|
8
8
|
APP_RAKEFILE = File.expand_path("spec/dummy/Rakefile", __dir__)
|
9
9
|
load "rails/tasks/engine.rake"
|
10
10
|
|
11
|
-
Bundler::GemHelper.install_tasks
|
12
|
-
|
13
11
|
require "rspec/core"
|
14
12
|
require "rspec/core/rake_task"
|
15
13
|
|
@@ -20,3 +18,9 @@ require "rubocop/rake_task"
|
|
20
18
|
RuboCop::RakeTask.new
|
21
19
|
|
22
20
|
task default: [:spec, :rubocop]
|
21
|
+
|
22
|
+
require "rake/release"
|
23
|
+
|
24
|
+
Rake::Release::Task.load_all do |spec|
|
25
|
+
spec.version_tag = "nulogy_message_bus_producer-v#{spec.version}"
|
26
|
+
end
|
@@ -0,0 +1,6 @@
|
|
1
|
+
class RenameTenantIdAndPublic < ActiveRecord::Migration[5.2]
|
2
|
+
def change
|
3
|
+
rename_column NulogyMessageBusProducer.subscription_events_table_name, :tenant_id, :company_uuid
|
4
|
+
rename_column NulogyMessageBusProducer.subscription_events_table_name, :public_subscription_id, :subscription_id
|
5
|
+
end
|
6
|
+
end
|
@@ -1,5 +1,4 @@
|
|
1
1
|
require "graphql"
|
2
|
-
require "strong_migrations"
|
3
2
|
|
4
3
|
require "nulogy_message_bus_producer/config"
|
5
4
|
require "nulogy_message_bus_producer/version"
|
@@ -15,39 +14,65 @@ module NulogyMessageBusProducer
|
|
15
14
|
end
|
16
15
|
|
17
16
|
def self.subscriptions_table_name=(table_name)
|
18
|
-
NulogyMessageBusProducer::
|
17
|
+
NulogyMessageBusProducer::Subscription.table_name = table_name
|
19
18
|
end
|
20
19
|
|
21
20
|
def self.subscriptions_table_name
|
22
|
-
NulogyMessageBusProducer::
|
21
|
+
NulogyMessageBusProducer::Subscription.table_name
|
23
22
|
end
|
24
23
|
|
25
24
|
def self.subscription_events_table_name=(table_name)
|
26
|
-
NulogyMessageBusProducer::
|
25
|
+
NulogyMessageBusProducer::SubscriptionEvent.table_name = table_name
|
27
26
|
end
|
28
27
|
|
29
28
|
def self.subscription_events_table_name
|
30
|
-
NulogyMessageBusProducer::
|
29
|
+
NulogyMessageBusProducer::SubscriptionEvent.table_name
|
31
30
|
end
|
32
31
|
|
33
|
-
def self.
|
34
|
-
config.
|
35
|
-
|
32
|
+
def self.context_for_subscription(subscription)
|
33
|
+
config.context_for_subscription(subscription)
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.trigger_event(schema, event_type, root_object)
|
37
|
+
schema_key = NulogyMessageBusProducer.resolve_schema_key(schema)
|
38
|
+
subscriptions = Subscription.where(event_type: event_type, schema_key: schema_key)
|
39
|
+
|
40
|
+
subscriptions.each do |subscription|
|
41
|
+
args = {
|
42
|
+
subscriptionId: subscription.id,
|
43
|
+
subscriptionGroupId: subscription.subscription_group_id,
|
44
|
+
topicName: subscription.topic_name
|
45
|
+
}
|
46
|
+
schema.subscriptions.trigger(event_type, args, root_object)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.resolve_schema(schema_key) # rubocop:disable Metrics/MethodLength
|
51
|
+
if config.registered_schemas.key?(schema_key)
|
52
|
+
config.registered_schemas[schema_key].constantize
|
53
|
+
elsif block_given?
|
54
|
+
yield
|
55
|
+
else
|
56
|
+
raise KeyError, <<~MESSAGE
|
36
57
|
The schema registry did not contain an entry for the schema key '#{schema_key}'.
|
37
58
|
|
38
59
|
Please register the schema first with `NulogyMessageBusProducer.schemas.register(schema_key, schema)`
|
39
|
-
MESSAGE
|
40
60
|
|
41
|
-
|
42
|
-
|
61
|
+
Schemas Registered:
|
62
|
+
#{config.registered_schemas.pretty_inspect}
|
63
|
+
MESSAGE
|
64
|
+
end
|
43
65
|
end
|
44
66
|
|
45
67
|
def self.resolve_schema_key(schema)
|
46
|
-
config.registered_schemas.invert.fetch(schema.
|
68
|
+
config.registered_schemas.invert.fetch(schema.name) do
|
47
69
|
raise KeyError, <<~MESSAGE
|
48
|
-
The schema registry did not contain an entry for the schema '#{schema.
|
70
|
+
The schema registry did not contain an entry for the schema '#{schema.name}'.
|
49
71
|
|
50
72
|
Please register the schema first with `NulogyMessageBusProducer.schemas.register(schema_key, schema)`
|
73
|
+
|
74
|
+
Schemas Registered:
|
75
|
+
#{config.registered_schemas.pretty_inspect}
|
51
76
|
MESSAGE
|
52
77
|
end
|
53
78
|
end
|
@@ -62,7 +87,7 @@ module NulogyMessageBusProducer
|
|
62
87
|
raise <<~MESSAGE
|
63
88
|
#{"#" * 80}
|
64
89
|
|
65
|
-
The GraphQL queries stored in
|
90
|
+
The GraphQL queries stored in subscriptions.query must always remain valid against the current schema.
|
66
91
|
If one is not valid, a domain event firing could fail and users would not be able to perform critical duties
|
67
92
|
(e.g. adding production).
|
68
93
|
|
@@ -101,7 +126,7 @@ module NulogyMessageBusProducer
|
|
101
126
|
FROM pg_tables
|
102
127
|
WHERE tablename = '#{NulogyMessageBusProducer.subscriptions_table_name}'
|
103
128
|
AND schemaname = 'public';
|
104
|
-
|
129
|
+
SQL
|
105
130
|
rescue # rubocop:disable Style/RescueStandardError
|
106
131
|
false
|
107
132
|
end
|
@@ -110,8 +135,11 @@ module NulogyMessageBusProducer
|
|
110
135
|
end
|
111
136
|
|
112
137
|
require "nulogy_message_bus_producer/application_record"
|
113
|
-
require "nulogy_message_bus_producer/
|
114
|
-
require "nulogy_message_bus_producer/
|
115
|
-
require "nulogy_message_bus_producer/
|
116
|
-
require "nulogy_message_bus_producer/
|
138
|
+
require "nulogy_message_bus_producer/base_subscription"
|
139
|
+
require "nulogy_message_bus_producer/subscription"
|
140
|
+
require "nulogy_message_bus_producer/subscription_event"
|
141
|
+
require "nulogy_message_bus_producer/repopulate_replication_slots"
|
117
142
|
require "nulogy_message_bus_producer/subscriber_graphql_schema_validator"
|
143
|
+
require "nulogy_message_bus_producer/subscriptions/postgres_transport"
|
144
|
+
require "nulogy_message_bus_producer/subscriptions/no_variables"
|
145
|
+
require "nulogy_message_bus_producer/subscriptions/risky_subscription_blocker"
|
@@ -5,7 +5,7 @@ module NulogyMessageBusProducer
|
|
5
5
|
# class AggregateRootCreated < NulogyMessageBusProducer::BasePublicSubscription
|
6
6
|
# field :aggregate_root, Domain::Public::Types::AggregateRootType, null: true
|
7
7
|
# end
|
8
|
-
class
|
8
|
+
class BaseSubscription < GraphQL::Schema::Subscription
|
9
9
|
argument :subscription_id, ID, required: true
|
10
10
|
argument :subscription_group_id, ID, required: true
|
11
11
|
argument :topic_name, String, required: true
|
@@ -5,6 +5,7 @@ module NulogyMessageBusProducer
|
|
5
5
|
FAILURE_MODES = [:raise, :soft_fail].freeze
|
6
6
|
|
7
7
|
attr_reader :registered_schemas
|
8
|
+
attr_writer :context_for_subscription
|
8
9
|
|
9
10
|
def initialize(options = {})
|
10
11
|
@registered_schemas = {}
|
@@ -13,6 +14,11 @@ module NulogyMessageBusProducer
|
|
13
14
|
update(options)
|
14
15
|
end
|
15
16
|
|
17
|
+
def context_for_subscription(subscription)
|
18
|
+
@context_for_subscription ||= ->(_) {}
|
19
|
+
@context_for_subscription.call(subscription)
|
20
|
+
end
|
21
|
+
|
16
22
|
def register_schema(schema:, key:)
|
17
23
|
@registered_schemas[key] = schema
|
18
24
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module NulogyMessageBusProducer
|
2
|
+
# This modules repopulates the replication slot (what debezium uses to populate Kafka)
|
3
|
+
# by re-generating the subscription events
|
4
|
+
module RepopulateReplicationSlots
|
5
|
+
def self.repopulate
|
6
|
+
table_name = NulogyMessageBusProducer::SubscriptionEvent.table_name
|
7
|
+
temp_table_name = "#{NulogyMessageBusProducer::SubscriptionEvent.table_name}_tmp_event_repopulation"
|
8
|
+
|
9
|
+
ActiveRecord::Base.connection.execute(<<~SQL)
|
10
|
+
BEGIN;
|
11
|
+
LOCK TABLE #{table_name} IN SHARE MODE;
|
12
|
+
|
13
|
+
DROP TABLE IF EXISTS #{temp_table_name};
|
14
|
+
CREATE TEMPORARY TABLE #{temp_table_name} AS
|
15
|
+
SELECT * FROM #{table_name} ORDER BY created_at ASC;
|
16
|
+
|
17
|
+
TRUNCATE #{table_name};
|
18
|
+
INSERT INTO #{table_name}
|
19
|
+
(SELECT * FROM #{temp_table_name} ORDER BY created_at ASC);
|
20
|
+
|
21
|
+
COMMIT;
|
22
|
+
SQL
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -13,7 +13,7 @@ module NulogyMessageBusProducer
|
|
13
13
|
@errors = []
|
14
14
|
end
|
15
15
|
|
16
|
-
def validate(subscription_or_subscriptions = NulogyMessageBusProducer::
|
16
|
+
def validate(subscription_or_subscriptions = NulogyMessageBusProducer::Subscription.all)
|
17
17
|
Array(subscription_or_subscriptions).each do |subscription|
|
18
18
|
schema = find_schema(subscription)
|
19
19
|
next unless schema
|