outboxer 0.1.0 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a573b50b1f20cc0966a9c6baef36216240ab20adee17adcf03da8a193436bf78
4
- data.tar.gz: 86573195530a94aac112fd0c52169a7564e97f0e677ba03c26d37b87e8eb7c02
3
+ metadata.gz: 99b8c36fa2db36b780ca732a63aba4df7f38ccd06d7a2403961a2cb483085153
4
+ data.tar.gz: 33db69284cdbc1fbf8398c98799deabd2a09b38eb863df2e2f88943db2bfa25d
5
5
  SHA512:
6
- metadata.gz: edd4bc1e08e57f742985e8382fa1202ee6df3812282f9b0d12054cce63406fb9a7101fa6a53d93f85205faf31ae324b35c3254c9ab586865959cdd1a2ccfe951
7
- data.tar.gz: 5d5196f1bdcadb519720c4d80b01852d9463f1d5844a90bd8447d392675b17357adae4deb4088b8210ea65f0ab4555707b0affee69aac20b0832b5ff0505b77a
6
+ metadata.gz: 5191c2ba363decec6729efd72dbaadd697241f74c11f07dfd722874fdc6df8be0b5001e696159f0ba17e2c744447dc11c256f90af17edb6bb03c68452e237c31
7
+ data.tar.gz: fdd2cfaecfa7d2972566ef340a164992325a1c08cc5810c7eee20a4218901082fb2ce45dcd10cb66ecd3747ffc55b5a3f135cb8a0acb7fb80a74f2c4d9e012c0
data/README.md CHANGED
@@ -1,10 +1,23 @@
1
1
  # Outboxer
2
2
 
3
- Creating a message in an SQL database and publishing it to redis via a sidekiq worker are two operations that cannot be combined into a single atomic operation. If either database fails, inconsistencies can occur.
3
+ ## Background
4
4
 
5
- In Outboxer, when a new message is created in your application's SQL database, Outboxer automatically creates a message in an outbox table with a status of 'unpublished', within the same local transaction. This ensures that either both operations succeed or both fail, preserving atomicity.
5
+ Typically in event driven Ruby on Rails applications:
6
6
 
7
- ## Installation
7
+ 1. a domain event model is created in an SQL database table
8
+ 2. a sidekiq worker is queued to handle the domain event asynchronously
9
+
10
+ ## Problem
11
+
12
+ As these two operations span multiple database types (SQL and redis), they can not be combined into a single atomic operation using a transaction. If either step fails, inconsistencies can occur.
13
+
14
+ ## Solution
15
+
16
+ Outboxer is a simple Ruby on Rails implementation of the [transactional outbox pattern](https://microservices.io/patterns/data/transactional-outbox.html): a well established solution to this problem. It ensures both operations succeed _eventually_, or both fail.
17
+
18
+ ### Getting started
19
+
20
+ ### Installation
8
21
 
9
22
  1. Add the Outboxer gem to your application's Gemfile:
10
23
 
@@ -24,18 +37,9 @@ bundle install
24
37
  bin/rails generate outboxer:install
25
38
  ```
26
39
 
27
- ## Usage
28
-
29
- ### 1. Migrate your database
30
-
31
- ```bash
32
- bin/rake db:migrate
33
- ```
34
-
35
-
36
- ### 2. Include Outboxer into existing model
40
+ ### Usage
37
41
 
38
- First, include `Outboxer::Outboxable` into your existing `Message` model:
42
+ #### 1. Include `Outboxer::Outboxable` into your existing Message model
39
43
 
40
44
  ```ruby
41
45
  class Message < ApplicationRecord
@@ -43,26 +47,50 @@ class Message < ApplicationRecord
43
47
  end
44
48
  ```
45
49
 
46
- ### 3. Update the publish block
50
+ #### 2. Update the generated bin/publish block e.g.
47
51
 
48
- By default, the `bin/publisher` script does not do anything with the message in its block.
52
+ ```ruby
53
+ Outboxer::Publisher.publish do |args|
54
+ args.logger.info("[#{message.id}] publishing")
49
55
 
50
- To customize this behavior, you should update the block in the `bin/publisher` file:
56
+ Worker.perform_async({ message_id: args.message.id })
51
57
 
52
- ```ruby
53
- Outboxer::Publisher.publish do |message:, logger:|
54
- Worker.perform_async({ message_id: message.id })
58
+ args.logger.info("[#{message.id}] published")
55
59
  end
56
60
  ```
57
61
 
58
- ### 3. Run the Publisher
62
+ #### 3. Migrate the database
59
63
 
60
- To start publishing messages, run the `bin/publisher` script:
64
+ ```bash
65
+ bin/rake db:migrate
66
+ ```
67
+
68
+ #### 4. Run the publisher
61
69
 
62
70
  ```bash
63
71
  bin/publisher
64
72
  ```
65
73
 
74
+ ## Implementation
75
+
76
+ 1. when an `ActiveRecord` model that includes `Outbox::Outboxable` is created, an `unpublished` `Outboxer::Message` is automatically created in the same transaction, with `Outboxer::Message#message` polymorphically assigned to the original model
77
+
78
+ 2. When the publisher finds a new `unpublished` `Outboxer::Message`, it yields to a user-supplied block and then:
79
+ - removes it if the task completes successfully
80
+ - marks it as failed and records the error if there's a problem
81
+
82
+ To see all the parts working together in a single place, check out the [publisher_spec.rb](https://github.com/fast-programmer/outboxer/blob/master/spec/outboxer/publisher_spec.rb)
83
+
84
+
85
+ ## Motivation
86
+
87
+ Outboxer was created with 4 key benefits in mind:
88
+
89
+ 1. speed of integration into existing Ruby on Rails applications (< 1 hour)
90
+ 2. comprehensive documentation that is easy to understand
91
+ 3. high reliability in production environments (100% code coverage)
92
+ 4. forever free to use in commerical applications (MIT licence)
93
+
66
94
  ## Contributing
67
95
 
68
96
  Bug reports and pull requests are welcome on GitHub at https://github.com/fast-programmer/outboxer. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/fast-programmer/outboxer/blob/main/CODE_OF_CONDUCT.md).
@@ -0,0 +1,124 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>
7
+ Class: Outboxer::Models::Exception
8
+
9
+ &mdash; Documentation by YARD 0.9.34
10
+
11
+ </title>
12
+
13
+ <link rel="stylesheet" href="../../css/style.css" type="text/css" />
14
+
15
+ <link rel="stylesheet" href="../../css/common.css" type="text/css" />
16
+
17
+ <script type="text/javascript">
18
+ pathId = "Outboxer::Models::Exception";
19
+ relpath = '../../';
20
+ </script>
21
+
22
+
23
+ <script type="text/javascript" charset="utf-8" src="../../js/jquery.js"></script>
24
+
25
+ <script type="text/javascript" charset="utf-8" src="../../js/app.js"></script>
26
+
27
+
28
+ </head>
29
+ <body>
30
+ <div class="nav_wrap">
31
+ <iframe id="nav" src="../../class_list.html?1"></iframe>
32
+ <div id="resizer"></div>
33
+ </div>
34
+
35
+ <div id="main" tabindex="-1">
36
+ <div id="header">
37
+ <div id="menu">
38
+
39
+ <a href="../../_index.html">Index (E)</a> &raquo;
40
+ <span class='title'><span class='object_link'><a href="../../Outboxer.html" title="Outboxer (module)">Outboxer</a></span></span> &raquo; <span class='title'><span class='object_link'><a href="../Models.html" title="Outboxer::Models (module)">Models</a></span></span>
41
+ &raquo;
42
+ <span class="title">Exception</span>
43
+
44
+ </div>
45
+
46
+ <div id="search">
47
+
48
+ <a class="full_list_link" id="class_list_link"
49
+ href="../../class_list.html">
50
+
51
+ <svg width="24" height="24">
52
+ <rect x="0" y="4" width="24" height="4" rx="1" ry="1"></rect>
53
+ <rect x="0" y="12" width="24" height="4" rx="1" ry="1"></rect>
54
+ <rect x="0" y="20" width="24" height="4" rx="1" ry="1"></rect>
55
+ </svg>
56
+ </a>
57
+
58
+ </div>
59
+ <div class="clear"></div>
60
+ </div>
61
+
62
+ <div id="content"><h1>Class: Outboxer::Models::Exception
63
+
64
+
65
+
66
+ </h1>
67
+ <div class="box_info">
68
+
69
+ <dl>
70
+ <dt>Inherits:</dt>
71
+ <dd>
72
+ <span class="inheritName">ActiveRecord::Base</span>
73
+
74
+ <ul class="fullTree">
75
+ <li>Object</li>
76
+
77
+ <li class="next">ActiveRecord::Base</li>
78
+
79
+ <li class="next">Outboxer::Models::Exception</li>
80
+
81
+ </ul>
82
+ <a href="#" class="inheritanceTree">show all</a>
83
+
84
+ </dd>
85
+ </dl>
86
+
87
+
88
+
89
+
90
+
91
+
92
+
93
+
94
+
95
+
96
+
97
+ <dl>
98
+ <dt>Defined in:</dt>
99
+ <dd>lib/outboxer/models/exception.rb</dd>
100
+ </dl>
101
+
102
+ </div>
103
+
104
+
105
+
106
+
107
+
108
+
109
+
110
+
111
+
112
+
113
+
114
+ </div>
115
+
116
+ <div id="footer">
117
+ Generated on Sat Aug 5 04:37:46 2023 by
118
+ <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
119
+ 0.9.34 (ruby-2.7.8).
120
+ </div>
121
+
122
+ </div>
123
+ </body>
124
+ </html>
@@ -0,0 +1,144 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>
7
+ Class: Outboxer::Models::Message
8
+
9
+ &mdash; Documentation by YARD 0.9.34
10
+
11
+ </title>
12
+
13
+ <link rel="stylesheet" href="../../css/style.css" type="text/css" />
14
+
15
+ <link rel="stylesheet" href="../../css/common.css" type="text/css" />
16
+
17
+ <script type="text/javascript">
18
+ pathId = "Outboxer::Models::Message";
19
+ relpath = '../../';
20
+ </script>
21
+
22
+
23
+ <script type="text/javascript" charset="utf-8" src="../../js/jquery.js"></script>
24
+
25
+ <script type="text/javascript" charset="utf-8" src="../../js/app.js"></script>
26
+
27
+
28
+ </head>
29
+ <body>
30
+ <div class="nav_wrap">
31
+ <iframe id="nav" src="../../class_list.html?1"></iframe>
32
+ <div id="resizer"></div>
33
+ </div>
34
+
35
+ <div id="main" tabindex="-1">
36
+ <div id="header">
37
+ <div id="menu">
38
+
39
+ <a href="../../_index.html">Index (M)</a> &raquo;
40
+ <span class='title'><span class='object_link'><a href="../../Outboxer.html" title="Outboxer (module)">Outboxer</a></span></span> &raquo; <span class='title'><span class='object_link'><a href="../Models.html" title="Outboxer::Models (module)">Models</a></span></span>
41
+ &raquo;
42
+ <span class="title">Message</span>
43
+
44
+ </div>
45
+
46
+ <div id="search">
47
+
48
+ <a class="full_list_link" id="class_list_link"
49
+ href="../../class_list.html">
50
+
51
+ <svg width="24" height="24">
52
+ <rect x="0" y="4" width="24" height="4" rx="1" ry="1"></rect>
53
+ <rect x="0" y="12" width="24" height="4" rx="1" ry="1"></rect>
54
+ <rect x="0" y="20" width="24" height="4" rx="1" ry="1"></rect>
55
+ </svg>
56
+ </a>
57
+
58
+ </div>
59
+ <div class="clear"></div>
60
+ </div>
61
+
62
+ <div id="content"><h1>Class: Outboxer::Models::Message
63
+
64
+
65
+
66
+ </h1>
67
+ <div class="box_info">
68
+
69
+ <dl>
70
+ <dt>Inherits:</dt>
71
+ <dd>
72
+ <span class="inheritName">ActiveRecord::Base</span>
73
+
74
+ <ul class="fullTree">
75
+ <li>Object</li>
76
+
77
+ <li class="next">ActiveRecord::Base</li>
78
+
79
+ <li class="next">Outboxer::Models::Message</li>
80
+
81
+ </ul>
82
+ <a href="#" class="inheritanceTree">show all</a>
83
+
84
+ </dd>
85
+ </dl>
86
+
87
+
88
+
89
+
90
+
91
+
92
+
93
+
94
+
95
+
96
+
97
+ <dl>
98
+ <dt>Defined in:</dt>
99
+ <dd>lib/outboxer/models/message.rb</dd>
100
+ </dl>
101
+
102
+ </div>
103
+
104
+
105
+
106
+ <h2>
107
+ Constant Summary
108
+ <small><a href="#" class="constants_summary_toggle">collapse</a></small>
109
+ </h2>
110
+
111
+ <dl class="constants">
112
+
113
+ <dt id="STATUS-constant" class="">STATUS =
114
+
115
+ </dt>
116
+ <dd><pre class="code"><span class='lbrace'>{</span>
117
+ <span class='label'>unpublished:</span> <span class='tstring'><span class='tstring_beg'>&quot;</span><span class='tstring_content'>unpublished</span><span class='tstring_end'>&quot;</span></span><span class='comma'>,</span>
118
+ <span class='label'>publishing:</span> <span class='tstring'><span class='tstring_beg'>&quot;</span><span class='tstring_content'>publishing</span><span class='tstring_end'>&quot;</span></span><span class='comma'>,</span>
119
+ <span class='label'>failed:</span> <span class='tstring'><span class='tstring_beg'>&quot;</span><span class='tstring_content'>failed</span><span class='tstring_end'>&quot;</span></span>
120
+ <span class='rbrace'>}</span><span class='period'>.</span><span class='id identifier rubyid_freeze'>freeze</span></pre></dd>
121
+
122
+ </dl>
123
+
124
+
125
+
126
+
127
+
128
+
129
+
130
+
131
+
132
+
133
+
134
+ </div>
135
+
136
+ <div id="footer">
137
+ Generated on Sat Aug 5 04:37:46 2023 by
138
+ <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
139
+ 0.9.34 (ruby-2.7.8).
140
+ </div>
141
+
142
+ </div>
143
+ </body>
144
+ </html>
@@ -0,0 +1,105 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>
7
+ Module: Outboxer::Models::Outboxable::ClassMethods
8
+
9
+ &mdash; Documentation by YARD 0.9.34
10
+
11
+ </title>
12
+
13
+ <link rel="stylesheet" href="../../../css/style.css" type="text/css" />
14
+
15
+ <link rel="stylesheet" href="../../../css/common.css" type="text/css" />
16
+
17
+ <script type="text/javascript">
18
+ pathId = "Outboxer::Models::Outboxable::ClassMethods";
19
+ relpath = '../../../';
20
+ </script>
21
+
22
+
23
+ <script type="text/javascript" charset="utf-8" src="../../../js/jquery.js"></script>
24
+
25
+ <script type="text/javascript" charset="utf-8" src="../../../js/app.js"></script>
26
+
27
+
28
+ </head>
29
+ <body>
30
+ <div class="nav_wrap">
31
+ <iframe id="nav" src="../../../class_list.html?1"></iframe>
32
+ <div id="resizer"></div>
33
+ </div>
34
+
35
+ <div id="main" tabindex="-1">
36
+ <div id="header">
37
+ <div id="menu">
38
+
39
+ <a href="../../../_index.html">Index (C)</a> &raquo;
40
+ <span class='title'><span class='object_link'><a href="../../../Outboxer.html" title="Outboxer (module)">Outboxer</a></span></span> &raquo; <span class='title'><span class='object_link'><a href="../../Models.html" title="Outboxer::Models (module)">Models</a></span></span> &raquo; <span class='title'><span class='object_link'><a href="../Outboxable.html" title="Outboxer::Models::Outboxable (module)">Outboxable</a></span></span>
41
+ &raquo;
42
+ <span class="title">ClassMethods</span>
43
+
44
+ </div>
45
+
46
+ <div id="search">
47
+
48
+ <a class="full_list_link" id="class_list_link"
49
+ href="../../../class_list.html">
50
+
51
+ <svg width="24" height="24">
52
+ <rect x="0" y="4" width="24" height="4" rx="1" ry="1"></rect>
53
+ <rect x="0" y="12" width="24" height="4" rx="1" ry="1"></rect>
54
+ <rect x="0" y="20" width="24" height="4" rx="1" ry="1"></rect>
55
+ </svg>
56
+ </a>
57
+
58
+ </div>
59
+ <div class="clear"></div>
60
+ </div>
61
+
62
+ <div id="content"><h1>Module: Outboxer::Models::Outboxable::ClassMethods
63
+
64
+
65
+
66
+ </h1>
67
+ <div class="box_info">
68
+
69
+
70
+
71
+
72
+
73
+
74
+
75
+
76
+
77
+
78
+
79
+ <dl>
80
+ <dt>Defined in:</dt>
81
+ <dd>lib/outboxer/models/outboxable.rb</dd>
82
+ </dl>
83
+
84
+ </div>
85
+
86
+
87
+
88
+
89
+
90
+
91
+
92
+
93
+
94
+
95
+ </div>
96
+
97
+ <div id="footer">
98
+ Generated on Sat Aug 5 04:37:46 2023 by
99
+ <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
100
+ 0.9.34 (ruby-2.7.8).
101
+ </div>
102
+
103
+ </div>
104
+ </body>
105
+ </html>