outboxer 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 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>