cyclone_lariat 0.4.0 → 1.0.0.rc1
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 +5 -5
- data/.github/workflows/gem-push.yml +4 -4
- data/.rubocop.yml +9 -5
- data/Gemfile.lock +123 -21
- data/Guardfile +42 -0
- data/README.md +417 -220
- data/bin/cyclone_lariat +75 -43
- data/cyclone_lariat.gemspec +10 -3
- data/lib/cyclone_lariat/clients/abstract.rb +40 -0
- data/lib/cyclone_lariat/clients/sns.rb +163 -0
- data/lib/cyclone_lariat/clients/sqs.rb +114 -0
- data/lib/cyclone_lariat/core.rb +21 -0
- data/lib/cyclone_lariat/errors.rb +16 -0
- data/lib/cyclone_lariat/fake.rb +19 -0
- data/lib/cyclone_lariat/generators/command.rb +53 -0
- data/lib/cyclone_lariat/generators/event.rb +52 -0
- data/lib/cyclone_lariat/generators/queue.rb +30 -0
- data/lib/cyclone_lariat/generators/topic.rb +29 -0
- data/lib/cyclone_lariat/messages/v1/abstract.rb +139 -0
- data/lib/cyclone_lariat/messages/v1/command.rb +20 -0
- data/lib/cyclone_lariat/messages/v1/event.rb +20 -0
- data/lib/cyclone_lariat/messages/v1/validator.rb +31 -0
- data/lib/cyclone_lariat/messages/v2/abstract.rb +149 -0
- data/lib/cyclone_lariat/messages/v2/command.rb +20 -0
- data/lib/cyclone_lariat/messages/v2/event.rb +20 -0
- data/lib/cyclone_lariat/messages/v2/validator.rb +39 -0
- data/lib/cyclone_lariat/middleware.rb +9 -6
- data/lib/cyclone_lariat/migration.rb +54 -117
- data/lib/cyclone_lariat/options.rb +52 -0
- data/lib/cyclone_lariat/presenters/graph.rb +54 -0
- data/lib/cyclone_lariat/presenters/queues.rb +41 -0
- data/lib/cyclone_lariat/presenters/subscriptions.rb +34 -0
- data/lib/cyclone_lariat/presenters/topics.rb +40 -0
- data/lib/cyclone_lariat/publisher.rb +25 -0
- data/lib/cyclone_lariat/repo/active_record/messages.rb +92 -0
- data/lib/cyclone_lariat/repo/active_record/versions.rb +28 -0
- data/lib/cyclone_lariat/repo/messages.rb +43 -0
- data/lib/cyclone_lariat/repo/messages_mapper.rb +49 -0
- data/lib/cyclone_lariat/repo/sequel/messages.rb +73 -0
- data/lib/cyclone_lariat/repo/sequel/versions.rb +28 -0
- data/lib/cyclone_lariat/repo/versions.rb +42 -0
- data/lib/cyclone_lariat/resources/queue.rb +167 -0
- data/lib/cyclone_lariat/resources/topic.rb +132 -0
- data/lib/cyclone_lariat/services/migrate.rb +51 -0
- data/lib/cyclone_lariat/services/rollback.rb +51 -0
- data/lib/cyclone_lariat/version.rb +1 -1
- data/lib/cyclone_lariat.rb +4 -11
- data/lib/tasks/console.rake +1 -1
- data/lib/tasks/cyclone_lariat.rake +10 -12
- data/lib/tasks/db.rake +0 -15
- metadata +127 -27
- data/config/db.example.rb +0 -9
- data/config/initializers/sequel.rb +0 -7
- data/db/migrate/01_add_uuid_extensions.rb +0 -15
- data/db/migrate/02_add_events.rb +0 -19
- data/db/migrate/03_add_versions.rb +0 -9
- data/docs/_imgs/graphviz_01.png +0 -0
- data/docs/_imgs/graphviz_02.png +0 -0
- data/docs/_imgs/graphviz_03.png +0 -0
- data/docs/_imgs/lariat.jpg +0 -0
- data/docs/_imgs/logic.png +0 -0
- data/docs/_imgs/sqs_sns_diagram.png +0 -0
- data/lib/cyclone_lariat/abstract/client.rb +0 -112
- data/lib/cyclone_lariat/abstract/message.rb +0 -98
- data/lib/cyclone_lariat/command.rb +0 -13
- data/lib/cyclone_lariat/configure.rb +0 -15
- data/lib/cyclone_lariat/event.rb +0 -13
- data/lib/cyclone_lariat/messages_mapper.rb +0 -46
- data/lib/cyclone_lariat/messages_repo.rb +0 -60
- data/lib/cyclone_lariat/queue.rb +0 -147
- data/lib/cyclone_lariat/sns_client.rb +0 -149
- data/lib/cyclone_lariat/sqs_client.rb +0 -93
- data/lib/cyclone_lariat/topic.rb +0 -113
data/README.md
CHANGED
@@ -1,17 +1,183 @@
|
|
1
1
|
# Cyclone lariat
|
2
2
|
|
3
3
|
This gem work in few scenarios:
|
4
|
-
- As middleware for [shoryuken](https://github.com/ruby-shoryuken/shoryuken).
|
5
|
-
-
|
6
|
-
- As a
|
7
|
-
-
|
4
|
+
- As middleware for [shoryuken](https://github.com/ruby-shoryuken/shoryuken).
|
5
|
+
- It saves all events to the database and also catches and throws all exceptions.
|
6
|
+
- As a middleware, it can log all incoming messages.
|
7
|
+
- As a [client](#client--publisher) that can send messages to SNS topics and SQS queues.
|
8
|
+
- Also it can help you with CI\CD to manage topics, queues and subscriptions such as database [migration](#Migrations).
|
8
9
|
|
9
10
|

|
10
11
|
|
12
|
+
## Install and configuration Cyclone Lariat
|
13
|
+
### Install
|
14
|
+
<details>
|
15
|
+
<summary>Sequel</summary>
|
16
|
+
|
17
|
+
#### Install with Sequel
|
18
|
+
Edit Gemfile:
|
19
|
+
```ruby
|
20
|
+
# Gemfile
|
21
|
+
gem 'sequel'
|
22
|
+
gem 'cyclone_lariat'
|
23
|
+
```
|
24
|
+
And run in console:
|
25
|
+
```bash
|
26
|
+
$ bundle install
|
27
|
+
$ cyclone_lariat install
|
28
|
+
```
|
29
|
+
</details>
|
30
|
+
<details>
|
31
|
+
<summary>ActiveRecord</summary>
|
32
|
+
|
33
|
+
#### Install with ActiveRecord
|
34
|
+
Edit Gemfile:
|
35
|
+
```ruby
|
36
|
+
# Gemfile
|
37
|
+
gem 'active_record'
|
38
|
+
gem 'cyclone_lariat'
|
39
|
+
```
|
40
|
+
And run in console:
|
41
|
+
```bash
|
42
|
+
$ bundle install
|
43
|
+
$ cyclone_lariat install --adapter=active_record
|
44
|
+
```
|
45
|
+
</details>
|
46
|
+
|
47
|
+
Last install command will create 2 files:
|
48
|
+
- ./lib/tasks/cyclone_lariat.rake - Rake tasks, for management migrations
|
49
|
+
- ./config/initializers/cyclone_lariat.rb - Configuration default values for cyclone lariat usage
|
50
|
+
|
51
|
+
|
52
|
+
### Configuration
|
53
|
+
<details>
|
54
|
+
<summary>Sequel</summary>
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
# frozen_string_literal: true
|
58
|
+
|
59
|
+
CycloneLariat.configure do |c|
|
60
|
+
c.version = 1 # api version
|
61
|
+
|
62
|
+
c.aws_key = ENV['AWS_KEY'] # aws key
|
63
|
+
c.aws_secret_key = ENV['AWS_SECRET_KEY'] # aws secret
|
64
|
+
c.aws_account_id = ENV['AWS_ACCOUNT_ID'] # aws account id
|
65
|
+
c.aws_region = ENV['AWS_REGION'] # aws region
|
66
|
+
|
67
|
+
c.publisher = ENV['APP_NAME'] # name of your publishers, usually name of your application
|
68
|
+
c.instance = ENV['INSTANCE'] # stage, production, test
|
69
|
+
c.driver = :sequel # driver Sequel
|
70
|
+
c.messages_dataset = DB[:async_messages] # Sequel dataset for store income messages (on receiver)
|
71
|
+
c.versions_dataset = DB[:lariat_versions] # Sequel dataset for versions of publisher migrations
|
72
|
+
c.fake_publish = ENV['INSTANCE'] == 'test' # when true, prevents messages from being published
|
73
|
+
end
|
74
|
+
```
|
75
|
+
|
76
|
+
#### Example migrations
|
77
|
+
Before using the event store, add and apply these migrations:
|
78
|
+
|
79
|
+
```ruby
|
80
|
+
Sequel.migration do
|
81
|
+
up do
|
82
|
+
run <<-SQL
|
83
|
+
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
84
|
+
SQL
|
85
|
+
end
|
11
86
|
|
12
|
-
|
13
|
-
|
14
|
-
|
87
|
+
down do
|
88
|
+
run <<-SQL
|
89
|
+
DROP EXTENSION IF EXISTS "uuid-ossp";
|
90
|
+
SQL
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
Sequel.migration do
|
95
|
+
change do
|
96
|
+
create_table :async_messages do
|
97
|
+
column :uuid, :uuid, primary_key: true
|
98
|
+
String :type, null: false
|
99
|
+
Integer :version, null: false
|
100
|
+
String :publisher, null: false
|
101
|
+
column :data, :json, null: false
|
102
|
+
String :client_error_message, null: true, default: nil
|
103
|
+
column :client_error_details, :json, null: true, default: nil
|
104
|
+
DateTime :sent_at, null: true, default: nil
|
105
|
+
DateTime :received_at, null: false, default: Sequel::CURRENT_TIMESTAMP
|
106
|
+
DateTime :processed_at, null: true, default: nil
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
Sequel.migration do
|
112
|
+
change do
|
113
|
+
create_table :lariat_versions do
|
114
|
+
Integer :version, null: false, unique: true
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
```
|
119
|
+
</details>
|
120
|
+
<details>
|
121
|
+
<summary>ActiveRecord</summary>
|
122
|
+
|
123
|
+
```ruby
|
124
|
+
# frozen_string_literal: true
|
125
|
+
|
126
|
+
CycloneLariat.configure do |c|
|
127
|
+
c.version = 1 # api version
|
128
|
+
|
129
|
+
c.aws_key = ENV['AWS_KEY'] # aws key
|
130
|
+
c.aws_secret_key = ENV['AWS_SECRET_KEY'] # aws secret
|
131
|
+
c.aws_account_id = ENV['AWS_ACCOUNT_ID'] # aws account id
|
132
|
+
c.aws_region = ENV['AWS_REGION'] # aws region
|
133
|
+
|
134
|
+
c.publisher = ENV['APP_NAME'] # name of your publishers, usually name of your application
|
135
|
+
c.instance = ENV['INSTANCE'] # stage, production, test
|
136
|
+
c.driver = :active_record # driver ActiveRecord
|
137
|
+
c.messages_dataset = CycloneLariatMessage # ActiveRecord model for store income messages (on receiver)
|
138
|
+
c.versions_dataset = CycloneLariatVersion # ActiveRecord model for versions of publisher migrations
|
139
|
+
c.fake_publish = ENV['INSTANCE'] == 'test' # when true, prevents messages from being published
|
140
|
+
end
|
141
|
+
```
|
142
|
+
|
143
|
+
#### Example migrations
|
144
|
+
Before using the event store, add and apply these migrations:
|
145
|
+
```ruby
|
146
|
+
# migrations
|
147
|
+
execute('CREATE EXTENSION IF NOT EXISTS "uuid-ossp"')
|
148
|
+
|
149
|
+
create_table :cyclone_lariat_messages, id: :uuid, primary_key: :uuid, default: -> { 'public.uuid_generate_v4()' } do |t|
|
150
|
+
t.string :kind, null: false
|
151
|
+
t.string :type, null: false
|
152
|
+
t.integer :version, null: false
|
153
|
+
t.string :publisher, null: false
|
154
|
+
t.jsonb :data, null: false
|
155
|
+
t.string :client_error_message, null: true, default: nil
|
156
|
+
t.jsonb :client_error_details, null: true, default: nil
|
157
|
+
t.datetime :sent_at, null: true, default: nil
|
158
|
+
t.datetime :received_at, null: false, default: -> { 'CURRENT_TIMESTAMP' }
|
159
|
+
t.datetime :processed_at, null: true, default: nil
|
160
|
+
end
|
161
|
+
|
162
|
+
create_table :cyclone_lariat_versions do |t|
|
163
|
+
t.integer :version, null: false, index: { unique: true }
|
164
|
+
end
|
165
|
+
|
166
|
+
# models
|
167
|
+
class CycloneLariatMessage < ActiveRecord::Base
|
168
|
+
self.inheritance_column = :_type_disabled
|
169
|
+
self.primary_key = 'uuid'
|
170
|
+
end
|
171
|
+
class CycloneLariatVersion < ActiveRecord::Base
|
172
|
+
end
|
173
|
+
```
|
174
|
+
</details>
|
175
|
+
|
176
|
+
If you are only using your application as a publisher, you may not need to set the _messages_dataset_ parameter.
|
177
|
+
|
178
|
+
## Client / Publisher
|
179
|
+
At first lets understand what the difference between SQS and SNS:
|
180
|
+
- Amazon Simple Queue Service (SQS) lets you send, store, and receive messages between software components at any
|
15
181
|
volume, without losing messages or requiring other services to be available.
|
16
182
|
- Amazon Simple Notification Service (SNS) sends notifications two ways Application2Person (like send sms).
|
17
183
|
And the second way is Application2Application, that's way more important for us. In this way you case use
|
@@ -19,158 +185,183 @@ SNS service like fanout.
|
|
19
185
|
|
20
186
|

|
21
187
|
|
22
|
-
For use **cyclone_lariat** as _Publisher_ lets make install CycloneLariat.
|
23
|
-
|
24
|
-
## Install cyclone_lariat
|
25
|
-
Edit Gemfile:
|
26
|
-
```ruby
|
27
|
-
# Gemfile
|
28
|
-
gem 'sequel'
|
29
|
-
gem 'cyclone_lariat'
|
30
|
-
```
|
31
|
-
And run in console:
|
32
|
-
```bash
|
33
|
-
$ bundle install
|
34
|
-
$ cyclone_lariat install
|
35
|
-
```
|
188
|
+
For use **cyclone_lariat** as _Publisher_ lets make install CycloneLariat.
|
36
189
|
|
37
|
-
|
38
|
-
- ./lib/tasks/cyclone_lariat.rake - Rake tasks, for management migrations
|
39
|
-
- ./config/initializers/cyclone_lariat.rb - Configuration default values for cyclone lariat usage
|
190
|
+
Before creating the first migration, let's explain what _CycloneLariat::Messages_ is.
|
40
191
|
|
41
|
-
|
42
|
-
|
43
|
-
#
|
192
|
+
### Messages
|
193
|
+
Message in Amazon SQS\SNS service it's a
|
194
|
+
[object](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-message-metadata.html#sqs-message-attributes)
|
195
|
+
that has several attributes. The main attributes are the **body**, which consists of the published
|
196
|
+
data. The body is a _String_, but we can use it as a _JSON_ object. **Cyclone_lariat** use by default scheme - version 1:
|
44
197
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
198
|
+
```json
|
199
|
+
// Scheme: version 1
|
200
|
+
{
|
201
|
+
"uuid": "f2ce3813-0905-4d81-a60e-f289f2431f50", // Uniq message identificator
|
202
|
+
"publisher": "sample_app", // Publisher application name
|
203
|
+
"request_id": "51285005-8a06-4181-b5fd-bf29f3b1a45a", // Optional: X-Request-Id
|
204
|
+
"type": "event_note_created", // Type of Event or Command
|
205
|
+
"version": 1, // Version of data structure
|
206
|
+
"data": {
|
207
|
+
"id": 12,
|
208
|
+
"text": "Sample of published data",
|
209
|
+
"attributes": ["one", "two", "three"]
|
210
|
+
},
|
211
|
+
"sent_at": "2022-11-09T11:42:18.203+01:00" // Time when message was sended in ISO8601 Standard
|
212
|
+
}
|
56
213
|
```
|
57
|
-
If you are only using your application as a publisher, you may not need to set the `events_dataset`
|
58
|
-
parameter.
|
59
214
|
|
60
|
-
|
215
|
+
Idea about X-Request-Id you can see at
|
216
|
+
[StackOverflow](https://stackoverflow.com/questions/25433258/what-is-the-x-request-id-http-header).
|
61
217
|
|
62
|
-
|
63
|
-
|
64
|
-
[object](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-message-metadata.html#sqs-message-attributes)
|
65
|
-
that has several attributes. The main attributes are the **body**, which consists of the published
|
66
|
-
data. The body is a _String_, but we can use it as a _JSON_ object. **Cyclone_lariat** use this scheme:
|
218
|
+
As you see, type has prefix 'event_' in cyclone lariat you has two kinds of messages - _Messages::V1::Event_ and
|
219
|
+
_Messages::V1::Command_.
|
67
220
|
|
221
|
+
If you want log all your messages you can use extended scheme - version 2:
|
68
222
|
```json
|
223
|
+
// Scheme: version 2
|
69
224
|
{
|
70
225
|
"uuid": "f2ce3813-0905-4d81-a60e-f289f2431f50", // Uniq message identificator
|
71
226
|
"publisher": "sample_app", // Publisher application name
|
72
|
-
"request_id": "51285005-8a06-4181-b5fd-bf29f3b1a45a", // Optional: X-Request-Id
|
227
|
+
"request_id": "51285005-8a06-4181-b5fd-bf29f3b1a45a", // Optional: X-Request-Id
|
73
228
|
"type": "event_note_created", // Type of Event or Command
|
74
|
-
"version":
|
229
|
+
"version": 2, // Version of data structure
|
230
|
+
"subject": {
|
231
|
+
"type": "user", // Subject type
|
232
|
+
"uuid": "a27c29e2-bbd3-490a-8f1b-caa4f8d902ef" // Subject uuid
|
233
|
+
},
|
234
|
+
"object": {
|
235
|
+
"type": "note", // Object type
|
236
|
+
"uuid": "f46e74db-3335-4c5e-b476-c2a87660a942" // Object uuid
|
237
|
+
},
|
75
238
|
"data": {
|
76
239
|
"id": 12,
|
77
240
|
"text": "Sample of published data",
|
78
|
-
"attributes": ["one", "two", "three"]
|
241
|
+
"attributes": ["one", "two", "three"]
|
79
242
|
},
|
80
|
-
"sent_at": "2022-11-09T11:42:18.203+01:00"
|
243
|
+
"sent_at": "2022-11-09T11:42:18.203+01:00" // Time when message was sended in ISO8601 Standard
|
81
244
|
}
|
82
245
|
```
|
246
|
+
#### Subject vs Object
|
83
247
|
|
84
|
-
|
85
|
-
|
248
|
+
The difference between scheme first and second version - is subject and object. This values need to help with actions log.
|
249
|
+
For example, user #42, write to support, "why he could not sign in". The messages log is:
|
86
250
|
|
87
|
-
|
251
|
+
| Subject | Action | Object |
|
252
|
+
|:---------|:------------|:----------|
|
253
|
+
| user #42 | sign_up | user #42 |
|
254
|
+
| user #42 | sign_in | user #42 |
|
255
|
+
| user #42 | create_note | note #769 |
|
256
|
+
| user #1 | ban | user #42 |
|
88
257
|
|
89
|
-
|
258
|
+
It is important to understand that user #42 can be both a subject and an object. And you should save both of these fields to keep track of the entire history of this user.
|
259
|
+
|
260
|
+
#### Command vs Event
|
90
261
|
Commands and events are both simple domain structures that contain solely data for reading. That means
|
91
262
|
they contain no behaviour or business logic.
|
92
263
|
|
93
264
|
A command is an object that is sent to the domain for a state change which is handled by a command
|
94
|
-
handler. They should be named with a verb in an imperative mood plus the aggregate name which it
|
265
|
+
handler. They should be named with a verb in an imperative mood plus the aggregate name which it
|
95
266
|
operates on. Such request can be rejected due to the data the command holds being invalid/inconsistent.
|
96
|
-
There should be exactly 1 handler for each command. Once the command has been executed, the consumer
|
267
|
+
There should be exactly 1 handler for each command. Once the command has been executed, the consumer
|
97
268
|
can then carry out whatever the task is depending on the output of the command.
|
98
269
|
|
99
|
-
An event is a statement of fact about what change has been made to the domain state. They are named
|
270
|
+
An event is a statement of fact about what change has been made to the domain state. They are named
|
100
271
|
with the aggregate name where the change took place plus the verb past-participle. An event happens off
|
101
|
-
the back of a command.
|
102
|
-
A command can emit any number of events. The sender of the event does not care who receives it or
|
272
|
+
the back of a command.
|
273
|
+
A command can emit any number of events. The sender of the event does not care who receives it or
|
103
274
|
whether it has been received at all.
|
104
275
|
|
105
276
|
### Publish
|
106
|
-
For publishing
|
277
|
+
For publishing _Messages::V1::Event_ or _Messages::V1::Commands_, you have two ways, send [_Message_](#Messages) directly:
|
107
278
|
|
108
279
|
```ruby
|
109
|
-
CycloneLariat.
|
110
|
-
#
|
280
|
+
CycloneLariat.configure do |config|
|
281
|
+
# Options app here
|
111
282
|
end
|
112
283
|
|
113
|
-
client = CycloneLariat::
|
284
|
+
client = CycloneLariat::Clients::Sns.new(publisher: 'auth', version: 1)
|
285
|
+
payload = {
|
286
|
+
first_name: 'John',
|
287
|
+
last_name: 'Doe',
|
288
|
+
mail: 'john.doe@example.com'
|
289
|
+
}
|
114
290
|
|
115
|
-
client.publish_command('register_user', data:
|
116
|
-
first_name: 'John',
|
117
|
-
last_name: 'Doe',
|
118
|
-
mail: 'john.doe@example.com'
|
119
|
-
}, fifo: false
|
120
|
-
)
|
291
|
+
client.publish_command('register_user', data: payload, fifo: false)
|
121
292
|
```
|
122
293
|
|
123
|
-
That's call, will generate a message body:
|
294
|
+
That's call, will generate a message body:
|
124
295
|
```json
|
125
296
|
{
|
126
297
|
"uuid": "f2ce3813-0905-4d81-a60e-f289f2431f50",
|
127
298
|
"publisher": "auth",
|
128
299
|
"type": "command_register_user",
|
129
|
-
"version":
|
300
|
+
"version": 1,
|
130
301
|
"data": {
|
131
|
-
"first_name": "John",
|
132
|
-
"last_name": "Doe",
|
133
|
-
"mail": "john.doe@example.com"
|
302
|
+
"first_name": "John",
|
303
|
+
"last_name": "Doe",
|
304
|
+
"mail": "john.doe@example.com"
|
134
305
|
},
|
135
|
-
"sent_at": "2022-11-09T11:42:18.203+01:00"
|
306
|
+
"sent_at": "2022-11-09T11:42:18.203+01:00" // The time the message was sent. ISO8601 standard.
|
136
307
|
}
|
137
308
|
```
|
138
309
|
|
139
|
-
Or
|
140
|
-
|
310
|
+
Or for second schema version code:
|
141
311
|
```ruby
|
142
|
-
|
312
|
+
CycloneLariat.configure do |config|
|
313
|
+
# Options app here
|
314
|
+
end
|
315
|
+
|
316
|
+
client = CycloneLariat::Clients::Sns.new(publisher: 'auth', version: 2)
|
317
|
+
|
318
|
+
client.publish_event(
|
319
|
+
'sign_up',
|
320
|
+
data: {
|
321
|
+
first_name: 'John',
|
322
|
+
last_name: 'Doe',
|
323
|
+
mail: 'john.doe@example.com'
|
324
|
+
},
|
325
|
+
subject: { type: 'user', uuid: '40250522-21c8-4fc7-9b0b-47d9666a4430'},
|
326
|
+
object: { type: 'user', uuid: '40250522-21c8-4fc7-9b0b-47d9666a4430'},
|
327
|
+
fifo: false
|
328
|
+
)
|
329
|
+
```
|
143
330
|
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
instance 'stage'
|
331
|
+
Or is it better to make your own client, like a [Repository](https://deviq.com/design-patterns/repository-pattern) pattern.
|
332
|
+
```ruby
|
333
|
+
require 'cyclone_lariat/publisher' # If require: false in Gemfile
|
148
334
|
|
335
|
+
class Publisher < CycloneLariat::Publisher
|
149
336
|
def email_is_created(mail)
|
150
|
-
publish event('email_is_created', data: { mail: mail }), fifo:
|
337
|
+
sns.publish event('email_is_created', data: { mail: mail }), fifo: false
|
151
338
|
end
|
152
339
|
|
153
340
|
def email_is_removed(mail)
|
154
|
-
publish event('email_is_removed', data: { mail: mail }), fifo:
|
341
|
+
sns.publish event('email_is_removed', data: { mail: mail }), fifo: false
|
155
342
|
end
|
156
343
|
|
157
|
-
|
158
344
|
def delete_user(mail)
|
159
|
-
publish command('delete_user', data: { mail: mail }), fifo: false
|
345
|
+
sns.publish command('delete_user', data: { mail: mail }), fifo: false
|
346
|
+
end
|
347
|
+
|
348
|
+
def welcome_message(mail, text)
|
349
|
+
sqs.publish command('welcome', data: {mail: mail, txt: text}), fifo: false
|
160
350
|
end
|
161
351
|
end
|
162
352
|
|
163
353
|
# Init repo
|
164
|
-
|
354
|
+
publisher = Publisher.new
|
165
355
|
|
166
356
|
# And send topics
|
167
|
-
|
168
|
-
|
169
|
-
|
357
|
+
publisher.email_is_created 'john.doe@example.com'
|
358
|
+
publisher.email_is_removed 'john.doe@example.com'
|
359
|
+
publisher.delete_user 'john.doe@example.com'
|
360
|
+
publisher.welcome_message 'john.doe@example.com', 'You are welcome'
|
170
361
|
```
|
171
362
|
|
172
|
-
|
173
|
-
An Amazon SNS topic and SQS queue is a logical access point that acts as a communication channel. Both
|
363
|
+
#### Topics and Queue
|
364
|
+
An Amazon SNS topic and SQS queue is a logical access point that acts as a communication channel. Both
|
174
365
|
of them has specific address ARN.
|
175
366
|
|
176
367
|
```
|
@@ -184,68 +375,77 @@ arn:aws:sqs:eu-west-1:247602342345:test-event-queue-cyclone_lariat-note_added-no
|
|
184
375
|
Split ARN:
|
185
376
|
- `arn:aws:sns` - Prefix for SNS Topics
|
186
377
|
- `arn:aws:sqs` - Prefix for SQS Queues
|
187
|
-
- `eu-west-1` -
|
188
|
-
[AWS Region](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-regions-availability-zones.html#concepts-regions)
|
378
|
+
- `eu-west-1` - [AWS Region](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-regions-availability-zones.html#concepts-regions)
|
189
379
|
- `247602342345` - [AWS account](https://docs.aws.amazon.com/IAM/latest/UserGuide/console_account-alias.html)
|
190
380
|
- `test-event-fanout-cyclone_lariat-note_added` - Topic \ Queue name
|
191
|
-
- `.fifo` - if Topic or queue is
|
192
|
-
|
193
|
-
has that suffix.
|
381
|
+
- `.fifo` - if Topic or queue is [FIFO](https://aws.amazon.com/blogs/aws/introducing-amazon-sns-fifo-first-in-first-out-pub-sub-messaging/), they must
|
382
|
+
has that suffix.
|
194
383
|
|
195
|
-
Region and
|
384
|
+
Region and account_id usually set using the **cyclone_lariat** [configuration](#Configuration).
|
196
385
|
|
197
|
-
|
386
|
+
#### Declaration for topic and queues names
|
198
387
|
In **cyclone_lariat** we have a declaration for defining topic and queue names.
|
199
388
|
This can help in organizing the order.
|
200
389
|
|
201
390
|
```ruby
|
202
|
-
CycloneLariat.
|
203
|
-
|
204
|
-
|
391
|
+
CycloneLariat.configure do |config|
|
392
|
+
config.instance = 'test'
|
393
|
+
config.publisher = 'cyclone_lariat'
|
205
394
|
# ...
|
206
395
|
end
|
207
396
|
|
208
|
-
CycloneLariat::
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
397
|
+
CycloneLariat::Clients::Sns.new.publish_command(
|
398
|
+
'register_user',
|
399
|
+
data: {
|
400
|
+
first_name: 'John',
|
401
|
+
last_name: 'Doe',
|
402
|
+
mail: 'john.doe@example.com'
|
403
|
+
},
|
404
|
+
fifo: false
|
213
405
|
)
|
214
406
|
|
215
|
-
# or in repository-like style:
|
216
|
-
|
217
|
-
class YourClient < CycloneLariat::SnsClient
|
407
|
+
# or in repository-like style:
|
408
|
+
class Publisher < CycloneLariat::Publisher
|
218
409
|
def register_user(first:, last:, mail:)
|
219
|
-
publish command(
|
410
|
+
sns.publish command(
|
411
|
+
'register_user',
|
412
|
+
data: {
|
413
|
+
mail: mail,
|
414
|
+
name: {
|
415
|
+
first: first,
|
416
|
+
last: last
|
417
|
+
}
|
418
|
+
}
|
419
|
+
), fifo: false
|
220
420
|
end
|
221
421
|
end
|
222
422
|
```
|
223
423
|
|
224
|
-
|
225
424
|
We will publish a message on this topic: `test-command-fanout-cyclone_lariat-register_user`.
|
226
425
|
|
227
426
|
Let's split the topic title:
|
228
427
|
- `test` - instance;
|
229
428
|
- `command` - kind - [event or command](#command-vs-event);
|
230
|
-
- `fanount` - resource type - fanout for SNS topics;
|
429
|
+
- `fanount` - resource type - fanout for SNS topics;
|
231
430
|
- `cyclone_lariat` - publisher name;
|
232
431
|
- `regiser_user` - message type.
|
233
432
|
|
234
433
|
For queues you also can define destination.
|
235
434
|
```ruby
|
236
|
-
CycloneLariat::
|
237
|
-
'register_user',
|
238
|
-
|
435
|
+
CycloneLariat::Clients::Sqs.new.publish_event(
|
436
|
+
'register_user',
|
437
|
+
data: { mail: 'john.doe@example.com' },
|
438
|
+
dest: :mailer,
|
439
|
+
fifo: false
|
239
440
|
)
|
240
441
|
|
442
|
+
# or in repository-like style:
|
241
443
|
|
242
|
-
|
243
|
-
|
244
|
-
class YourClient < CycloneLariat::SnsClient
|
444
|
+
class YourClient < CycloneLariat::Clients::Sns
|
245
445
|
# ...
|
246
|
-
|
446
|
+
|
247
447
|
def register_user(first:, last:, mail:)
|
248
|
-
publish event('register_user', data: { mail: mail }), fifo:
|
448
|
+
publish event('register_user', data: { mail: mail }), fifo: false
|
249
449
|
end
|
250
450
|
end
|
251
451
|
```
|
@@ -264,40 +464,81 @@ You also can sent message to queue with custom name. But this way does not recom
|
|
264
464
|
|
265
465
|
```ruby
|
266
466
|
# Directly
|
267
|
-
CycloneLariat::
|
268
|
-
'register_user', data: { mail: 'john.doe@example.com' },
|
269
|
-
dest: :mailer, topic: 'custom_topic_name.fifo', fifo:
|
467
|
+
CycloneLariat::Clients::Sqs.new.publish_event(
|
468
|
+
'register_user', data: { mail: 'john.doe@example.com' },
|
469
|
+
dest: :mailer, topic: 'custom_topic_name.fifo', fifo: false
|
270
470
|
)
|
271
471
|
|
272
472
|
# Repository
|
273
|
-
class
|
473
|
+
class Publisher < CycloneLariat::Publisher
|
274
474
|
# ...
|
275
475
|
|
276
476
|
def register_user(first:, last:, mail:)
|
277
|
-
publish event('register_user', data: { mail: mail }),
|
278
|
-
topic: 'custom_topic_name.fifo', fifo:
|
477
|
+
publish event('register_user', data: { mail: mail }),
|
478
|
+
topic: 'custom_topic_name.fifo', fifo: false
|
279
479
|
end
|
280
480
|
end
|
281
481
|
```
|
282
|
-
Will publish message on queue: `
|
482
|
+
Will publish message on queue: `custom_topic_name`
|
283
483
|
|
284
|
-
# Migrations
|
285
484
|
|
286
|
-
|
287
|
-
|
485
|
+
### FIFO and no FIFO
|
486
|
+
The main idea you can read on [AWS Docs](https://aws.amazon.com/blogs/aws/introducing-amazon-sns-fifo-first-in-first-out-pub-sub-messaging/).
|
487
|
+
|
488
|
+
FIFO message should consist two fields:
|
489
|
+
- `group_id` - In each topic, the FIFO sequence is defined only within one group.
|
490
|
+
[AWS Docs](https://docs.aws.amazon.com/sns/latest/dg/fifo-message-grouping.html)
|
491
|
+
- `deduplication_id` - Within the same group, a unique identifier must be defined for each message.
|
492
|
+
[AWS Docs](https://docs.aws.amazon.com/sns/latest/dg/fifo-message-dedup.html)
|
493
|
+
|
494
|
+
The unique identifier can definitely be the entire message. In this case, you
|
495
|
+
do not need to pass the deduplication_id parameter. But you must create a queue
|
496
|
+
with the `content_based_deduplication` parameter in migration.
|
497
|
+
|
288
498
|
|
289
499
|
```ruby
|
290
|
-
|
500
|
+
class Publisher < CycloneLariat::Publisher
|
501
|
+
def user_created(mail:, uuid:)
|
502
|
+
sns.publish event('user_created', data: {
|
503
|
+
user: {
|
504
|
+
uuid: uuid,
|
505
|
+
mail: mail
|
506
|
+
},
|
507
|
+
},
|
508
|
+
deduplication_id: uuid,
|
509
|
+
group_id: uuid),
|
510
|
+
fifo: true
|
511
|
+
end
|
291
512
|
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
513
|
+
def user_mail_changed(mail:, uuid:)
|
514
|
+
sns.publish event('user_mail_created', data: {
|
515
|
+
user: {
|
516
|
+
uuid: uuid,
|
517
|
+
mail: mail
|
518
|
+
},
|
519
|
+
},
|
520
|
+
deduplication_id: mail,
|
521
|
+
group_id: uuid),
|
522
|
+
fifo: true
|
297
523
|
end
|
298
524
|
end
|
299
525
|
```
|
300
|
-
|
526
|
+
|
527
|
+
### Tests for publishers
|
528
|
+
|
529
|
+
Instead of stub all requests to AWS services, you can set up cyclone lariat for make fake publishing.
|
530
|
+
|
531
|
+
```ruby
|
532
|
+
CycloneLariat.configure do |c|
|
533
|
+
# ...
|
534
|
+
c.fake_publish = ENV['INSTANCE'] == 'test' # when true, prevents messages from being published
|
535
|
+
end
|
536
|
+
```
|
537
|
+
|
538
|
+
## Migrations
|
539
|
+
|
540
|
+
With **cyclone_lariat** you can use migrations that can create, delete, and subscribe to your queues and topics, just like database migrations do.
|
541
|
+
Before using this function, you must complete the **cyclone_lariat** [configuration](#Configuration).
|
301
542
|
|
302
543
|
```bash
|
303
544
|
$ cyclone_lariat generate migration user_created
|
@@ -312,14 +553,16 @@ This command should create a migration file, let's edit it.
|
|
312
553
|
|
313
554
|
class UserCreatedQueue < CycloneLariat::Migration
|
314
555
|
def up
|
315
|
-
create queue(:user_created, dest: :mailer, fifo: true)
|
556
|
+
create queue(:user_created, dest: :mailer, content_based_deduplication: true, fifo: true)
|
316
557
|
end
|
317
558
|
|
318
559
|
def down
|
319
|
-
delete queue(:user_created, dest: :mailer, fifo: true)
|
560
|
+
delete queue(:user_created, dest: :mailer, content_based_deduplication: true, fifo: true)
|
320
561
|
end
|
321
562
|
end
|
322
563
|
```
|
564
|
+
The `content_based_dedupplication` parameter can only be specified for FIFO resources. When true, the whole message is
|
565
|
+
used as the unique message identifier instead of the `deduplication_id` key.
|
323
566
|
|
324
567
|
To apply migration use:
|
325
568
|
```bash
|
@@ -331,7 +574,7 @@ To decline migration use:
|
|
331
574
|
$ rake cyclone_lariat:rollback
|
332
575
|
```
|
333
576
|
|
334
|
-
Since the SNS\SQS management does not support an ACID transaction (in the sense of a database),
|
577
|
+
Since the SNS\SQS management does not support an ACID transaction (in the sense of a database),
|
335
578
|
I highly recommend using the atomic schema:
|
336
579
|
|
337
580
|
```ruby
|
@@ -388,10 +631,9 @@ class UserCreatedSubscription < CycloneLariat::Migration
|
|
388
631
|
end
|
389
632
|
```
|
390
633
|
|
634
|
+
### Example: one-to-many
|
391
635
|
|
392
|
-
|
393
|
-
|
394
|
-
The first example is when your _registration_ service creates new user. You also have two services:
|
636
|
+
The first example is when your _registration_ service creates new user. You also have two services:
|
395
637
|
_mailer_ - sending a welcome email, and _statistics_ service.
|
396
638
|
|
397
639
|
```ruby
|
@@ -399,7 +641,7 @@ create topic(:user_created, fifo: true)
|
|
399
641
|
create queue(:user_created, dest: :mailer, fifo: true)
|
400
642
|
create queue(:user_created, dest: :stat, fifo: true)
|
401
643
|
|
402
|
-
subscribe topic: topic(:user_created, fifo: true),
|
644
|
+
subscribe topic: topic(:user_created, fifo: true),
|
403
645
|
endpoint: queue(:user_created, dest: :mailer, fifo: true)
|
404
646
|
|
405
647
|
|
@@ -408,9 +650,9 @@ subscribe topic: topic(:user_created, fifo: true),
|
|
408
650
|
```
|
409
651
|

|
410
652
|
|
411
|
-
|
653
|
+
### Example: many-to-one
|
412
654
|
|
413
|
-
The second example is when you have three services: _registration_ - creates new users, _order_
|
655
|
+
The second example is when you have three services: _registration_ - creates new users, _order_
|
414
656
|
service - allows you to create new orders, _statistics_ service collects all statistics.
|
415
657
|
|
416
658
|
```ruby
|
@@ -418,21 +660,21 @@ create topic(:user_created, fifo: false)
|
|
418
660
|
create topic(:order_created, fifo: false)
|
419
661
|
create queue(publisher: :any, dest: :statistic, fifo: false)
|
420
662
|
|
421
|
-
subscribe topic: topic(:user_created, fifo: false),
|
663
|
+
subscribe topic: topic(:user_created, fifo: false),
|
422
664
|
endpoint: queue(publisher: :any, dest: :statistic, fifo: false)
|
423
665
|
|
424
|
-
subscribe topic: topic(:order_created, fifo: false),
|
666
|
+
subscribe topic: topic(:order_created, fifo: false),
|
425
667
|
endpoint: queue(publisher: :any, dest: :statistic, fifo: false)
|
426
668
|
```
|
427
669
|

|
428
670
|
|
429
|
-
If queue receives messages from multiple sources you must specify publisher as `:any`. If the
|
671
|
+
If queue receives messages from multiple sources you must specify publisher as `:any`. If the
|
430
672
|
subscriber receives messages with different types, `cyclone_lariat` uses a specific keyword - `all`.
|
431
673
|
|
432
|
-
|
674
|
+
### Example fanout-to-fanout
|
433
675
|
|
434
|
-
For better organisation you can subscribe topic on topic. For example, you have _management_panel_
|
435
|
-
and _client_panel_ services. Each of these services can register a user with predefined roles.
|
676
|
+
For better organisation you can subscribe topic on topic. For example, you have _management_panel_
|
677
|
+
and _client_panel_ services. Each of these services can register a user with predefined roles.
|
436
678
|
And you want to send this information to the _mailer_ and _statistics_ services.
|
437
679
|
|
438
680
|
```ruby
|
@@ -442,13 +684,13 @@ create topic(:user_created, publisher: :any, fifo: false)
|
|
442
684
|
create queue(:user_created, publisher: :any, dest: :mailer, fifo: false)
|
443
685
|
create queue(:user_created, publisher: :any, dest: :stat, fifo: false)
|
444
686
|
|
445
|
-
subscribe topic: topic(:
|
687
|
+
subscribe topic: topic(:client_created, fifo: false),
|
446
688
|
endpoint: topic(:user_created, publisher: :any, fifo: false)
|
447
689
|
|
448
|
-
subscribe topic: topic(:manager_created, fifo: false),
|
690
|
+
subscribe topic: topic(:manager_created, fifo: false),
|
449
691
|
endpoint: topic(:user_created, publisher: :any, fifo: false)
|
450
692
|
|
451
|
-
subscribe topic: topic(:user_created, publisher: :any, fifo: false),
|
693
|
+
subscribe topic: topic(:user_created, publisher: :any, fifo: false),
|
452
694
|
endpoint: queue(:user_created, publisher: :any, dest: :mailer, fifo: false)
|
453
695
|
|
454
696
|
subscribe topic: topic(:user_created, publisher: :any, fifo: false),
|
@@ -475,7 +717,7 @@ We recommend locate migration on:
|
|
475
717
|
- **queue** - on Subscriber side;
|
476
718
|
- **subscription** - on Subscriber side.
|
477
719
|
|
478
|
-
|
720
|
+
## Console tasks
|
479
721
|
|
480
722
|
```bash
|
481
723
|
$ cyclone_lariat install - install cyclone_lariat
|
@@ -489,15 +731,15 @@ $ rake cyclone_lariat:rollback[version] # Rollback topics for SQS/SNS
|
|
489
731
|
$ rake cyclone_lariat:graph # Make graph
|
490
732
|
```
|
491
733
|
|
492
|
-
Graph generated in [grpahviz](https://graphviz.org/) format for the entry scheme. You should install
|
734
|
+
Graph generated in [grpahviz](https://graphviz.org/) format for the entry scheme. You should install
|
493
735
|
it on your system. For convert it in png use:
|
494
736
|
```bash
|
495
737
|
$ rake cyclone_lariat:list:subscriptions | dot -Tpng -o foo.png
|
496
738
|
```
|
497
739
|
|
498
|
-
|
740
|
+
## Subscriber
|
499
741
|
|
500
|
-
This is gem work like middleware for [shoryuken](https://github.com/ruby-shoryuken/shoryuken). It save all events to
|
742
|
+
This is gem work like middleware for [shoryuken](https://github.com/ruby-shoryuken/shoryuken). It save all events to
|
501
743
|
database. And catch and produce all exceptions.
|
502
744
|
|
503
745
|
The logic of lariat as a subscriber. Imagine that you are working with an http server. And it gives you various response
|
@@ -510,15 +752,21 @@ codes. You have the following processing:
|
|
510
752
|
|
511
753
|

|
512
754
|
|
513
|
-
|
755
|
+
## Middleware
|
514
756
|
If you use middleware:
|
515
757
|
- Store all events to dataset
|
516
758
|
- Notify every input sqs message
|
517
759
|
- Notify every error
|
518
760
|
|
519
761
|
```ruby
|
762
|
+
require 'sequel'
|
520
763
|
require 'cyclone_lariat/middleware' # If require: false in Gemfile
|
521
|
-
require '
|
764
|
+
require 'luna_park/notifiers/log'
|
765
|
+
|
766
|
+
require_relative './config/initializers/cyclone_lariat'
|
767
|
+
|
768
|
+
Shoryuken::Logging.logger = Logger.new STDOUT
|
769
|
+
Shoryuken::Logging.logger.level = Logger::INFO
|
522
770
|
|
523
771
|
class Receiver
|
524
772
|
include Shoryuken::Worker
|
@@ -529,9 +777,7 @@ class Receiver
|
|
529
777
|
body_parser: ->(sqs_msg) {
|
530
778
|
JSON.parse(sqs_msg.body, symbolize_names: true)
|
531
779
|
},
|
532
|
-
queue:
|
533
|
-
# or
|
534
|
-
# queue: CycloneLariat::SqsClient.new.queue('user_added', fifo: true).name
|
780
|
+
queue: CycloneLariat.queue(:user_created, dest: :stat, fifo: true).name
|
535
781
|
|
536
782
|
server_middleware do |chain|
|
537
783
|
# Options dataset, errors_notifier and message_notifier is optionals.
|
@@ -539,7 +785,7 @@ class Receiver
|
|
539
785
|
# If you dont define dataset - middleware does not store events in db
|
540
786
|
chain.add CycloneLariat::Middleware,
|
541
787
|
dataset: DB[:events],
|
542
|
-
errors_notifier:
|
788
|
+
errors_notifier: LunaPark::Notifiers::Sentry.new,
|
543
789
|
message_notifier: LunaPark::Notifiers::Log.new(min_lvl: :debug, format: :pretty_json)
|
544
790
|
end
|
545
791
|
|
@@ -548,76 +794,27 @@ class Receiver
|
|
548
794
|
|
549
795
|
def perform(sqs_message, sqs_message_body)
|
550
796
|
# Your logic here
|
551
|
-
|
797
|
+
|
552
798
|
# If you want to raise business error
|
553
799
|
raise UserIsNotRegistered.new(first_name: 'John', last_name: 'Doe')
|
554
800
|
end
|
555
801
|
end
|
556
802
|
```
|
557
803
|
|
558
|
-
##
|
559
|
-
Before using the event store, add and apply these two migrations:
|
560
|
-
|
561
|
-
```ruby
|
562
|
-
|
563
|
-
# First one
|
564
|
-
|
565
|
-
Sequel.migration do
|
566
|
-
up do
|
567
|
-
run <<-SQL
|
568
|
-
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
569
|
-
SQL
|
570
|
-
end
|
571
|
-
|
572
|
-
down do
|
573
|
-
run <<-SQL
|
574
|
-
DROP EXTENSION IF EXISTS "uuid-ossp";
|
575
|
-
SQL
|
576
|
-
end
|
577
|
-
end
|
578
|
-
|
579
|
-
# The second one:
|
580
|
-
Sequel.migration do
|
581
|
-
change do
|
582
|
-
create_table :async_messages do
|
583
|
-
column :uuid, :uuid, primary_key: true
|
584
|
-
String :type, null: false
|
585
|
-
Integer :version, null: false
|
586
|
-
String :publisher, null: false
|
587
|
-
column :data, :json, null: false
|
588
|
-
String :client_error_message, null: true, default: nil
|
589
|
-
column :client_error_details, :json, null: true, default: nil
|
590
|
-
DateTime :sent_at, null: true, default: nil
|
591
|
-
DateTime :received_at, null: false, default: Sequel::CURRENT_TIMESTAMP
|
592
|
-
DateTime :processed_at, null: true, default: nil
|
593
|
-
end
|
594
|
-
end
|
595
|
-
end
|
596
|
-
```
|
597
|
-
|
598
|
-
And don't forget to add it to the config file:
|
599
|
-
|
600
|
-
```ruby
|
601
|
-
# 'config/initializers/cyclone_lariat.rb'
|
602
|
-
CycloneLariat.tap do |cl|
|
603
|
-
cl.events_dataset = DB[:async_messages]
|
604
|
-
end
|
605
|
-
```
|
606
|
-
|
607
|
-
### Rake tasks
|
804
|
+
## Rake tasks
|
608
805
|
|
609
|
-
For simplify write some Rake tasks you can use `CycloneLariat::Repo`.
|
806
|
+
For simplify write some Rake tasks you can use `CycloneLariat::Repo::Messages`.
|
610
807
|
|
611
808
|
```ruby
|
612
809
|
# For retry all unprocessed
|
613
810
|
|
614
|
-
CycloneLariat.new
|
811
|
+
CycloneLariat::Repo::Messages.new.each_unprocessed do |event|
|
615
812
|
# Your logic here
|
616
813
|
end
|
617
814
|
|
618
815
|
# For retry all events with client errors
|
619
816
|
|
620
|
-
CycloneLariat.new
|
817
|
+
CycloneLariat::Repo::Messages.new.each_with_client_errors do |event|
|
621
818
|
# Your logic here
|
622
819
|
end
|
623
820
|
```
|