sidekiq-transaction_guard 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +3 -0
- data/MIT_LICENSE.txt +20 -0
- data/README.md +158 -0
- data/VERSION +1 -0
- data/lib/sidekiq-transaction_guard.rb +1 -0
- data/lib/sidekiq/transaction_guard.rb +117 -0
- data/lib/sidekiq/transaction_guard/database_cleaner.rb +26 -0
- data/lib/sidekiq/transaction_guard/middleware.rb +80 -0
- data/lib/sidekiq/transaction_guard/version.rb +7 -0
- data/sidekiq-transaction_guard.gemspec +45 -0
- metadata +169 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 294e37857447f017a08970a3d0430042c69e0afee645d25ce3f2350ce93fb9e6
|
4
|
+
data.tar.gz: ee1c16813ca499c9c146fa3db6dcc4f7d3f1c4b8417769ca1fa9635cdcd793c0
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b533961d40cc75425032fbb39ecf51a216bca82cf50f3e5d45d2b6c0a628b7da38b03e4c1bc5387711d4a4ddb36a120626826156d42020fd768ff6adb0e16069
|
7
|
+
data.tar.gz: 520048075eb69133f17233a5d8ef037e8d8f6ce4c886be5c1f398b966154bb7c0e27ed1a6e4c4694e17d46abd1e69a0651b5f72cd44c13520a2b8d07bbb05907
|
data/CHANGELOG.md
ADDED
data/MIT_LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2018 Brian Durand, Winston Durand
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,158 @@
|
|
1
|
+
# Sidekiq::TransactionGuard
|
2
|
+
|
3
|
+
[![Build Status](https://travis-ci.com/bdurand/sidekiq-transaction_guard.svg?branch=master)](https://travis-ci.com/bdurand/sidekiq-transaction_guard)
|
4
|
+
[![Maintainability](https://api.codeclimate.com/v1/badges/17bbf5cb6eda022028fe/maintainability)](https://codeclimate.com/github/bdurand/sidekiq-transaction_guard/maintainability)
|
5
|
+
|
6
|
+
You should never call a Sidekiq worker that relies on the state of the database from within a database transaction. You will end up with a race condition since the worker could kick off before the transaction is actually written to the database. This gem can be used to highlight where your code may be scheduling workers in an indeterminate state.
|
7
|
+
|
8
|
+
## The Problem
|
9
|
+
|
10
|
+
Consider this case:
|
11
|
+
|
12
|
+
```ruby
|
13
|
+
class Post < ActiveRecord::Base
|
14
|
+
# BAD: DO NOT DO THIS
|
15
|
+
after_create do
|
16
|
+
PostCreatedWorker.perform_async(id)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class PostCreatedWorker
|
21
|
+
include Sidekiq::Worker
|
22
|
+
|
23
|
+
def perform(post_id)
|
24
|
+
post = Post.find_by(id: post_id)
|
25
|
+
if post
|
26
|
+
do_something_with(post)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
```
|
31
|
+
|
32
|
+
In this case, the `PostCreatedWorker` job will be created for a new `Post` record in Sidekiq before the data is actually written to the database. If Sidekiq picks up that worker and tries to execute it before the transaction is committed, `Post.find_by(id: post_id)` won't find anything and the worker will exit without performing it's task. Even if the worker doesn't need to read from the database, there is still a chance for an error to rollback the transaction leaving a possibility of workers running that should not have been scheduled.
|
33
|
+
|
34
|
+
To solve this, workers like this should be invoked in ActiveRecord from an `after_commit` callback. These callbacks are guaranteed to only execute after the data has been written to the database. However, as your application grows and gets more complicated, it can be difficult to ensure that workers are not being scheduled in the middle of transactions.
|
35
|
+
|
36
|
+
Switching from callbacks to service objects won't help you either, because service objects can be wrapped in transactions as well. The will just give you a new problem to solve.
|
37
|
+
|
38
|
+
```ruby
|
39
|
+
class CreatePost
|
40
|
+
def initialize(attributes)
|
41
|
+
@attributes = attributes
|
42
|
+
end
|
43
|
+
|
44
|
+
def call
|
45
|
+
post = Post.create!(attributes)
|
46
|
+
PostCreatedWorker.perform_async(post.id)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Still calling `perform_async` inside a transaction.
|
51
|
+
Post.transaction do
|
52
|
+
CreatePost.new(post_1_attributes)
|
53
|
+
CreatePost.new(post_2_attributes)
|
54
|
+
end
|
55
|
+
```
|
56
|
+
|
57
|
+
## The Solution
|
58
|
+
|
59
|
+
You can use this gem to add Sidekiq client middleware that will either warn you or raise an error when workers are scheduled inside of a database transaction. You can do this by simply adding this to your application's initialization code:
|
60
|
+
|
61
|
+
```ruby
|
62
|
+
require 'sidekiq/transaction-guard'
|
63
|
+
|
64
|
+
Sidekiq.configure_client do |config|
|
65
|
+
config.client_middleware do |chain|
|
66
|
+
chain.add(Sidekiq::TransactionGuard::Middleware)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
```
|
70
|
+
|
71
|
+
### Mode
|
72
|
+
|
73
|
+
By default, the behavior is to log that a worker is being scheduled inside of a transaction to the `Sidekiq.logger`. If you are running a test suite, you may want to expose the problematic calls by either raising errors or logging the calls to standard error. The mode can be one of `[:warn, :stderr, :error, :disabled]`.
|
74
|
+
|
75
|
+
```ruby
|
76
|
+
# Raise errors
|
77
|
+
Sidekiq::TransactionGuard.mode = :error
|
78
|
+
|
79
|
+
# Log to STDERR
|
80
|
+
Sidekiq::TransactionGuard.mode = :stderr
|
81
|
+
|
82
|
+
# Log to Sidekiq.logger
|
83
|
+
Sidekiq::TransactionGuard.mode = :warn
|
84
|
+
|
85
|
+
# Disable entirely
|
86
|
+
Sidekiq::TransactionGuard.mode = :disabled
|
87
|
+
```
|
88
|
+
|
89
|
+
You can also set the mode on individual worker classes with `sidekiq_options transaction_guard: mode`.
|
90
|
+
|
91
|
+
```ruby
|
92
|
+
class SomeWorker
|
93
|
+
include Sidekiq::Worker
|
94
|
+
|
95
|
+
sidekiq_options transaction_guard: :error
|
96
|
+
end
|
97
|
+
```
|
98
|
+
|
99
|
+
|
100
|
+
You can use the `:disabled` mode to allow individual worker classes to be scheduled inside of transactions where the worker logic doesn't care about the state of the database. For instance, if you use a Sidekiq worker to report errors, you would want to all it inside of transactions. If you don't control the worker you want to change the mode on, you simply call this in an initializer:
|
101
|
+
|
102
|
+
```ruby
|
103
|
+
SomeWorker.sidekiq_options.merge(transaction_guard: :disabled)
|
104
|
+
```
|
105
|
+
|
106
|
+
You could
|
107
|
+
|
108
|
+
### Notification Handlers
|
109
|
+
|
110
|
+
You can also set a block to be called if a worker is scheduled inside of a transaction. This can be useful if you use an error logging service to notify you of problematic calls in production so you can fix them.
|
111
|
+
|
112
|
+
```ruby
|
113
|
+
# Define a global notify handler
|
114
|
+
Sidekiq::TransactionGuard.notify do |job|
|
115
|
+
# Do what ever you need to. The job argument will be a Sidekiq job hash.
|
116
|
+
end
|
117
|
+
|
118
|
+
# Define on a per worker level
|
119
|
+
class SomeWorker
|
120
|
+
include Sidekiq::Worker
|
121
|
+
|
122
|
+
sidekiq_options notify_in_transaction: -> (job) { # Do something }
|
123
|
+
end
|
124
|
+
|
125
|
+
# Disable the global notification handler on a worker
|
126
|
+
class SomeOtherWorker
|
127
|
+
include Sidekiq::Worker
|
128
|
+
|
129
|
+
sidekiq_options notify_in_transaction: false
|
130
|
+
end
|
131
|
+
```
|
132
|
+
|
133
|
+
## Multiple Databases
|
134
|
+
|
135
|
+
Out of the box, this gem only deals with one database and monitors the connection pool returned by `ActiveRecord::Base.connection`. If you have multiple databases (or even multiple connections to the same database) that you want to track, you need to tell `Sidekiq::TransactionGuard` about them.
|
136
|
+
|
137
|
+
```ruby
|
138
|
+
class MyClass < ActiveRecord::Base
|
139
|
+
# This estabilishes a new connection pool.
|
140
|
+
establish_connection(configurations["otherdb"])
|
141
|
+
end
|
142
|
+
|
143
|
+
Sidekiq::TransactionGuard.add_connection_class(MyClass)
|
144
|
+
```
|
145
|
+
|
146
|
+
The class is used to get to the connection pool used for the class. You only need to add one class per connection pool, so you don't need to add any subclasses of `MyClass`.
|
147
|
+
|
148
|
+
## Transaction Fixtures In Tests
|
149
|
+
|
150
|
+
If you're using transaction fixtures in your tests, there will always be a database transaction open. If you're using [DatabaseCleaner](https://github.com/DatabaseCleaner/database_cleaner) in your tests, you just need to include this snippet in your test suite initializer:
|
151
|
+
|
152
|
+
```ruby
|
153
|
+
require 'sidekiq/transaction_guard/database_cleaner'
|
154
|
+
```
|
155
|
+
|
156
|
+
This will add the appropriate code so that the surrounding transaction in the test suite is ignored (i.e. workers will only warn/error if there is more than one open transaction).
|
157
|
+
|
158
|
+
If you're using something else for your transactional fixtures or have some other weird setup, look in the `lib/sidekiq_transaction_guard/database_cleaner.rb` file for an example of what you need to do.
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.0.0
|
@@ -0,0 +1 @@
|
|
1
|
+
require_relative "sidekiq/transaction_guard"
|
@@ -0,0 +1,117 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'sidekiq'
|
4
|
+
require 'set'
|
5
|
+
require 'thread'
|
6
|
+
|
7
|
+
require_relative 'transaction_guard/middleware'
|
8
|
+
|
9
|
+
module Sidekiq
|
10
|
+
module TransactionGuard
|
11
|
+
class InsideTransactionError < StandardError
|
12
|
+
end
|
13
|
+
|
14
|
+
@lock = Mutex.new
|
15
|
+
@connection_classes = Set.new
|
16
|
+
@notify = nil
|
17
|
+
@mode = :warn
|
18
|
+
|
19
|
+
class << self
|
20
|
+
VALID_MODES = [:warn, :stderr, :error, :disabled].freeze
|
21
|
+
|
22
|
+
# Set the global mode to one of `[:warn, :stderr, :error, :disabled]`. The
|
23
|
+
# default mode is `:warn`. This controls the behavior of workers enqueued
|
24
|
+
# inside of transactions.
|
25
|
+
# * :warn - Log to Sidekiq.logger
|
26
|
+
# * :stderr - Log to STDERR
|
27
|
+
# * :error - Throw a `Sidekiq::TransactionGuard::InsideTransactionError`
|
28
|
+
# * :disabled - Allow workers inside of transactions
|
29
|
+
def mode=(symbol)
|
30
|
+
if VALID_MODES.include?(symbol)
|
31
|
+
@mode = symbol
|
32
|
+
else
|
33
|
+
raise ArgumentError.new("mode must be one of #{VALID_MODES.inspect}")
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Return the current mode.
|
38
|
+
def mode
|
39
|
+
@mode
|
40
|
+
end
|
41
|
+
|
42
|
+
# Define the global notify block. This block will be called with a Sidekiq
|
43
|
+
# job hash for all jobs enqueued inside transactions if the mode is `:warn`
|
44
|
+
# or `:stderr`.
|
45
|
+
def notify(&block)
|
46
|
+
@notify = block
|
47
|
+
end
|
48
|
+
|
49
|
+
# Return the block set as the notify handler with a call to `notify`.
|
50
|
+
def notify_block
|
51
|
+
@notify
|
52
|
+
end
|
53
|
+
|
54
|
+
# Add a class that maintains it's own connection pool to the connections
|
55
|
+
# being monitored for open transactions. You don't need to add `ActiveRecord::Base`
|
56
|
+
# or subclasses. Only the base class that establishes a new connection pool
|
57
|
+
# with a call to `establish_connection` needs to be added.
|
58
|
+
def add_connection_class(connection_class)
|
59
|
+
@lock.synchronize{ @connection_classes << connection_class }
|
60
|
+
end
|
61
|
+
|
62
|
+
# Return true if any connection is currently inside of a transaction.
|
63
|
+
def in_transaction?
|
64
|
+
connection_classes = [ActiveRecord::Base]
|
65
|
+
unless @connection_classes.empty?
|
66
|
+
connection_classes.concat(@lock.synchronize{ @connection_classes.to_a })
|
67
|
+
end
|
68
|
+
connection_classes.any? do |connection_class|
|
69
|
+
connection_pool = connection_class.connection_pool
|
70
|
+
connection = connection_class.connection if connection_pool.active_connection?
|
71
|
+
if connection
|
72
|
+
connection.open_transactions > allowed_transaction_level(connection_class)
|
73
|
+
else
|
74
|
+
false
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# This method call needs to be wrapped around tests that use transactional fixtures.
|
80
|
+
# It sets up data structures used to track the number of open transactions.
|
81
|
+
def testing(&block)
|
82
|
+
var = :sidekiq_rails_transaction_guard
|
83
|
+
save_val = Thread.current[var]
|
84
|
+
begin
|
85
|
+
Thread.current[var] = (save_val ? save_val.dup : {})
|
86
|
+
yield
|
87
|
+
ensure
|
88
|
+
Thread.current[var] = save_val
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# This method needs to be called to set the allowed transaction level for a connection
|
93
|
+
# class (see `add_connection_class` for more info). The current transaction level
|
94
|
+
# for that class' connection will be set as the zero point. This method can only
|
95
|
+
# be called inside a block wrapped with the `testing` method.
|
96
|
+
def set_allowed_transaction_level(connection_class)
|
97
|
+
connection_counts = Thread.current[:sidekiq_rails_transaction_guard]
|
98
|
+
unless connection_counts
|
99
|
+
raise("set_allowed_transaction_level is only allowed inside a testing block")
|
100
|
+
end
|
101
|
+
connection_counts[connection_class.name] = connection_class.connection.open_transactions if connection_counts
|
102
|
+
end
|
103
|
+
|
104
|
+
private
|
105
|
+
|
106
|
+
def allowed_transaction_level(connection_class)
|
107
|
+
connection_counts = Thread.current[:sidekiq_rails_transaction_guard]
|
108
|
+
(connection_counts && connection_counts[connection_class.name]) || 0
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# Configure the default transaction guard mode for known testing environments.
|
115
|
+
if ENV["RAILS_ENV"] == "test" || ENV["RACK_ENV"] == "test"
|
116
|
+
Sidekiq::TransactionGuard.mode = :stderr
|
117
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'sidekiq/transaction_guard'
|
2
|
+
require 'database_cleaner'
|
3
|
+
require 'database_cleaner/active_record/transaction'
|
4
|
+
|
5
|
+
module Sidekiq
|
6
|
+
module TransactionGuard
|
7
|
+
module DatabaseCleaner
|
8
|
+
# Override the start method to set the base number of allowed transactions to
|
9
|
+
# the current level. Anything above this number will then be considered to be
|
10
|
+
# in a transaction.
|
11
|
+
def start
|
12
|
+
retval = super
|
13
|
+
Sidekiq::TransactionGuard.set_allowed_transaction_level(connection_class)
|
14
|
+
retval
|
15
|
+
end
|
16
|
+
|
17
|
+
# Wrap the `Sidekiq::TransactionGuard.testing` which sets up the data structures
|
18
|
+
# needed for custom counting of the transaction level within a test block.
|
19
|
+
def cleaning(&block)
|
20
|
+
Sidekiq::TransactionGuard.testing{ super(&block) }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
::DatabaseCleaner::ActiveRecord::Transaction.send(:prepend, Sidekiq::TransactionGuard::DatabaseCleaner)
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Sidekiq
|
4
|
+
module TransactionGuard
|
5
|
+
# Sidekiq client middleware that will warn/error when workers are called inside of
|
6
|
+
# a database transaction.
|
7
|
+
#
|
8
|
+
# This middleware can read `sidekiq_options` set on the worker for
|
9
|
+
# `:transaction_guard` and `:notify_in_transaction` which will override
|
10
|
+
# the default behavior set in `Sidekiq::TransactionGuard.mode` and
|
11
|
+
# `Sidekiq::TransactionGuard.notify` respectively.
|
12
|
+
class Middleware
|
13
|
+
def call(worker_class, job, queue, redis_pool)
|
14
|
+
# Check if we need to log this. Also, convert worker_class to its actual class
|
15
|
+
log_transaction(worker_class.constantize, job) if in_transaction?
|
16
|
+
|
17
|
+
yield
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def worker_mode(worker_class)
|
23
|
+
read_sidekiq_option(worker_class, :transaction_guard) || Sidekiq::TransactionGuard.mode
|
24
|
+
end
|
25
|
+
|
26
|
+
def in_transaction?
|
27
|
+
Sidekiq::TransactionGuard.in_transaction?
|
28
|
+
end
|
29
|
+
|
30
|
+
def notify_block(worker_class)
|
31
|
+
handler = read_sidekiq_option(worker_class, :notify_in_transaction)
|
32
|
+
if handler
|
33
|
+
handler
|
34
|
+
elsif handler == false
|
35
|
+
nil
|
36
|
+
else
|
37
|
+
Sidekiq::TransactionGuard.notify_block
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def read_sidekiq_option(worker_class, option_name)
|
42
|
+
options = worker_class.sidekiq_options_hash
|
43
|
+
options[option_name.to_s] if options
|
44
|
+
end
|
45
|
+
|
46
|
+
def notify!(worker_class, job)
|
47
|
+
notify_handler = notify_block(worker_class)
|
48
|
+
if notify_handler
|
49
|
+
begin
|
50
|
+
notify_handler.call(job)
|
51
|
+
rescue => e
|
52
|
+
if Sidekiq.logger
|
53
|
+
Sidekiq.logger.error(e)
|
54
|
+
else
|
55
|
+
STDERR.write("ERROR on Sidekiq::TransactionGuard notify block for #{worker_class}: #{e.inspect}\n")
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def log_transaction(worker_class, job)
|
62
|
+
mode = worker_mode(worker_class)
|
63
|
+
if mode != :disabled
|
64
|
+
message = "#{worker_class.name} was called from inside a database transaction"
|
65
|
+
if mode == :error
|
66
|
+
raise Sidekiq::TransactionGuard::InsideTransactionError.new(message)
|
67
|
+
else
|
68
|
+
logger = Sidekiq.logger unless mode == :stderr
|
69
|
+
if logger
|
70
|
+
logger.warn(message)
|
71
|
+
else
|
72
|
+
STDERR.write("WARNING #{message}\n")
|
73
|
+
end
|
74
|
+
notify!(worker_class, job)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "lib/sidekiq/transaction_guard/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "sidekiq-transaction_guard"
|
7
|
+
spec.version = Sidekiq::TransactionGuard::VERSION
|
8
|
+
spec.authors = ["Brian Durand", "Winston Durand"]
|
9
|
+
spec.email = ["bbdurand@gmail.com", "me@winstondurand.com"]
|
10
|
+
|
11
|
+
spec.summary = "Protect from accidentally invoking Sidekiq jobs when there are open database transactions"
|
12
|
+
spec.homepage = "https://github.com/bdurand/sidekiq-transaction_guard"
|
13
|
+
spec.license = "MIT"
|
14
|
+
|
15
|
+
# Specify which files should be added to the gem when it is released.
|
16
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
17
|
+
ignore_files = %w(
|
18
|
+
.gitignore
|
19
|
+
.travis.yml
|
20
|
+
Appraisals
|
21
|
+
Gemfile
|
22
|
+
Gemfile.lock
|
23
|
+
Rakefile
|
24
|
+
gemfiles/
|
25
|
+
spec/
|
26
|
+
)
|
27
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
28
|
+
`git ls-files -z`.split("\x0").reject{ |f| ignore_files.any?{ |path| f.start_with?(path) } }
|
29
|
+
end
|
30
|
+
spec.bindir = "exe"
|
31
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
32
|
+
spec.require_paths = ["lib"]
|
33
|
+
|
34
|
+
spec.required_ruby_version = '>= 2.2.2'
|
35
|
+
|
36
|
+
spec.add_dependency "activerecord", ">= 4.0"
|
37
|
+
spec.add_dependency "sidekiq", ">= 3.0"
|
38
|
+
|
39
|
+
spec.add_development_dependency "bundler", "~> 1.10"
|
40
|
+
spec.add_development_dependency "rake"
|
41
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
42
|
+
spec.add_development_dependency "database_cleaner"
|
43
|
+
spec.add_development_dependency "sqlite3"
|
44
|
+
spec.add_development_dependency "appraisal"
|
45
|
+
end
|
metadata
ADDED
@@ -0,0 +1,169 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sidekiq-transaction_guard
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Brian Durand
|
8
|
+
- Winston Durand
|
9
|
+
autorequire:
|
10
|
+
bindir: exe
|
11
|
+
cert_chain: []
|
12
|
+
date: 2018-10-08 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: activerecord
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - ">="
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '4.0'
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '4.0'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: sidekiq
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - ">="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '3.0'
|
35
|
+
type: :runtime
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '3.0'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: bundler
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - "~>"
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '1.10'
|
49
|
+
type: :development
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - "~>"
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '1.10'
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: rake
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
type: :development
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
- !ruby/object:Gem::Dependency
|
71
|
+
name: rspec
|
72
|
+
requirement: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - "~>"
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '3.0'
|
77
|
+
type: :development
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - "~>"
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '3.0'
|
84
|
+
- !ruby/object:Gem::Dependency
|
85
|
+
name: database_cleaner
|
86
|
+
requirement: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0'
|
91
|
+
type: :development
|
92
|
+
prerelease: false
|
93
|
+
version_requirements: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - ">="
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: '0'
|
98
|
+
- !ruby/object:Gem::Dependency
|
99
|
+
name: sqlite3
|
100
|
+
requirement: !ruby/object:Gem::Requirement
|
101
|
+
requirements:
|
102
|
+
- - ">="
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
version: '0'
|
105
|
+
type: :development
|
106
|
+
prerelease: false
|
107
|
+
version_requirements: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - ">="
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '0'
|
112
|
+
- !ruby/object:Gem::Dependency
|
113
|
+
name: appraisal
|
114
|
+
requirement: !ruby/object:Gem::Requirement
|
115
|
+
requirements:
|
116
|
+
- - ">="
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: '0'
|
119
|
+
type: :development
|
120
|
+
prerelease: false
|
121
|
+
version_requirements: !ruby/object:Gem::Requirement
|
122
|
+
requirements:
|
123
|
+
- - ">="
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
description:
|
127
|
+
email:
|
128
|
+
- bbdurand@gmail.com
|
129
|
+
- me@winstondurand.com
|
130
|
+
executables: []
|
131
|
+
extensions: []
|
132
|
+
extra_rdoc_files: []
|
133
|
+
files:
|
134
|
+
- CHANGELOG.md
|
135
|
+
- MIT_LICENSE.txt
|
136
|
+
- README.md
|
137
|
+
- VERSION
|
138
|
+
- lib/sidekiq-transaction_guard.rb
|
139
|
+
- lib/sidekiq/transaction_guard.rb
|
140
|
+
- lib/sidekiq/transaction_guard/database_cleaner.rb
|
141
|
+
- lib/sidekiq/transaction_guard/middleware.rb
|
142
|
+
- lib/sidekiq/transaction_guard/version.rb
|
143
|
+
- sidekiq-transaction_guard.gemspec
|
144
|
+
homepage: https://github.com/bdurand/sidekiq-transaction_guard
|
145
|
+
licenses:
|
146
|
+
- MIT
|
147
|
+
metadata: {}
|
148
|
+
post_install_message:
|
149
|
+
rdoc_options: []
|
150
|
+
require_paths:
|
151
|
+
- lib
|
152
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
153
|
+
requirements:
|
154
|
+
- - ">="
|
155
|
+
- !ruby/object:Gem::Version
|
156
|
+
version: 2.2.2
|
157
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
158
|
+
requirements:
|
159
|
+
- - ">="
|
160
|
+
- !ruby/object:Gem::Version
|
161
|
+
version: '0'
|
162
|
+
requirements: []
|
163
|
+
rubyforge_project:
|
164
|
+
rubygems_version: 2.7.6
|
165
|
+
signing_key:
|
166
|
+
specification_version: 4
|
167
|
+
summary: Protect from accidentally invoking Sidekiq jobs when there are open database
|
168
|
+
transactions
|
169
|
+
test_files: []
|