mongoid-locking 1.1.1 → 1.3.0

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: d7295a06524fd71fe15ceb6891b452cbd698459e7ebd9105dcad9b36fd438952
4
- data.tar.gz: 241baea11c9c0bd16525f94d72d3358ea5eeae2cd1f24d9a715bdc5cb085b5dc
3
+ metadata.gz: 896cdc8de472341f894c24adbedf92e07bc88a0d13c602f75233e052391d505b
4
+ data.tar.gz: 545fb0a99ca2280c696ec2eccab487a1f6b8a5bd898be20707f2cb3406296ff8
5
5
  SHA512:
6
- metadata.gz: 2c109c5fcbe7110c1b2c7507e3bf651f72659639b5b39b1e91acdf07997ffc772661ac627ab3a9c46ebeb4cdd4b119440724d9de6ab8f0507ee8e2d20b59e248
7
- data.tar.gz: eed71b3a4074bbdd5a9753317d12f39a0bbc56879f523a71d209547e1270e0a29d7fe4007b8e1b18831d1d6f66ee9a9c38f74964a0ddc49a3cff1b86a8136993
6
+ metadata.gz: 51aa29f8661bb0e4c257c22137eff2342b308deb47c8c1c1b68ffeaec32767985e55741011978d427c2333f422abe18e4731a0e91bab186df71180d8e6734e15
7
+ data.tar.gz: 3b23274fb66538af4dfd938af8f6824dce5d3439f82cbabf6041d15d6f36f1d1fa4e37195d3e5ef3151bae04014d51577bb77ccb5701c44ed7b84b951aeb1ecf
data/.rubocop.yml CHANGED
@@ -30,3 +30,6 @@ RSpec/DescribeClass:
30
30
 
31
31
  RSpec/MultipleExpectations:
32
32
  Enabled: false
33
+
34
+ RSpec/ExampleLength:
35
+ Max: 10
data/CHANGELOG.md CHANGED
@@ -1,4 +1,18 @@
1
- ## [Unreleased]
1
+ ## [1.3.0]
2
+
3
+ - Add: delay between retries for `with_locking` method [#8](https://github.com/fullhealthmedical/mongoid-locking/pull/8)
4
+
5
+ ## [1.2.0]
6
+
7
+ - Add: with_locking method [#6](https://github.com/fullhealthmedical/mongoid-locking/pull/6)
8
+
9
+ ## [1.1.1]
10
+
11
+ - Fix: Fix update embedded association [#5](https://github.com/fullhealthmedical/mongoid-locking/pull/5)
12
+
13
+ ## [1.1.0]
14
+
15
+ - Add: Support for Mongoid 7.3 [#3](https://github.com/fullhealthmedical/mongoid-locking/pull/3)
2
16
 
3
17
  ## [0.1.0] - 2022-08-03
4
18
 
data/Gemfile.lock CHANGED
@@ -1,20 +1,19 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- mongoid-locking (1.1.1)
4
+ mongoid-locking (1.3.0)
5
5
  mongoid (~> 7.2)
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
9
9
  specs:
10
- activemodel (6.1.7.6)
11
- activesupport (= 6.1.7.6)
12
- activesupport (6.1.7.6)
10
+ activemodel (7.0.8)
11
+ activesupport (= 7.0.8)
12
+ activesupport (7.0.8)
13
13
  concurrent-ruby (~> 1.0, >= 1.0.2)
14
14
  i18n (>= 1.6, < 2)
15
15
  minitest (>= 5.1)
16
16
  tzinfo (~> 2.0)
17
- zeitwerk (~> 2.3)
18
17
  ast (2.4.2)
19
18
  bson (4.15.0)
20
19
  byebug (11.1.3)
@@ -24,10 +23,10 @@ GEM
24
23
  concurrent-ruby (~> 1.0)
25
24
  json (2.6.2)
26
25
  minitest (5.20.0)
27
- mongo (2.19.2)
26
+ mongo (2.19.3)
28
27
  bson (>= 4.14.1, < 5.0.0)
29
- mongoid (7.2.6)
30
- activemodel (>= 5.1, < 6.2)
28
+ mongoid (7.5.4)
29
+ activemodel (>= 5.1, < 7.1, != 7.0.0)
31
30
  mongo (>= 2.10.5, < 3.0.0)
32
31
  ruby2_keywords (~> 0.0.5)
33
32
  parallel (1.22.1)
@@ -69,7 +68,6 @@ GEM
69
68
  tzinfo (2.0.6)
70
69
  concurrent-ruby (~> 1.0)
71
70
  unicode-display_width (2.2.0)
72
- zeitwerk (2.6.12)
73
71
 
74
72
  PLATFORMS
75
73
  arm64-darwin-21
data/README.md CHANGED
@@ -19,7 +19,7 @@ If bundler is not being used to manage dependencies, install the gem by executin
19
19
 
20
20
  - Include `Mongoid::Locking` module
21
21
 
22
- ```
22
+ ```ruby
23
23
  class Order
24
24
  include Mongoid::Document
25
25
  include Mongoid::Locking
@@ -28,16 +28,66 @@ If bundler is not being used to manage dependencies, install the gem by executin
28
28
 
29
29
  - Handle `Mongoid::StaleObjectError` when performing updates
30
30
 
31
- ```
31
+ ```ruby
32
32
  # ...
33
33
  def update_order
34
- order.update(attributes)
34
+ Order.update(attributes)
35
35
  rescue Mongoid::StaleObjectError => e
36
36
  add_error("This order has been changed ...")
37
37
  end
38
38
  end
39
39
  ```
40
40
 
41
+ - Use `with_locking` to automatically handle `Mongoid::StaleObjectError`
42
+
43
+ By default, `with_locking` will retry the update 3 times in case of a
44
+ `Mongoid::StaleObjectError`.
45
+
46
+ ```ruby
47
+ def process_with_locking
48
+ Order.with_locking do
49
+ # Ensure the block is idempotent
50
+ # Reload object(s) within the block to get the latest changes
51
+ order.reload
52
+ # Your code here
53
+ # ...
54
+ end
55
+ end
56
+ ```
57
+
58
+ Adjust the `max_retries` parameter to control the number of retry attempts in
59
+ case of a `Mongoid::StaleObjectError`.
60
+
61
+ ```ruby
62
+ # ...
63
+ def update_order
64
+ order.with_locking(max_retries: 2) do
65
+ order.update(attributes)
66
+ end
67
+ end
68
+ end
69
+ ```
70
+
71
+ - Use `with_locking` at the instance level to automatically reload the instance
72
+ on each retry
73
+
74
+ ```ruby
75
+ def process_with_locking
76
+ order.with_locking do
77
+ # The instance is automatically reloaded for each retry
78
+ # Your code here
79
+ # ...
80
+ end
81
+ end
82
+ ````
83
+
84
+ The with_locking method at the instance level combines class-level locking with
85
+ automatic reloading on each retry. This ensures that the instance reflects the
86
+ latest changes, providing seamless control over optimistic locking.
87
+
88
+ NOTE: The `with_locking` method will add a delay between retries to avoid
89
+ contention. [More info](https://github.com/fullhealthmedical/mongoid-locking/pull/8).
90
+
41
91
  ## Development
42
92
 
43
93
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -0,0 +1,67 @@
1
+ module Mongoid
2
+ module Locking
3
+ ##
4
+ # Gives the ability to retry a block of code a specified number of times
5
+ # when a Mongoid::StaleObjectError is raised.
6
+ module Retry
7
+ def self.included(base)
8
+ base.extend(ClassMethods)
9
+ end
10
+
11
+ ##
12
+ # Retries the block of code a specified number of times when a
13
+ # Mongoid::StaleObjectError is raised.
14
+ #
15
+ # This method will reload the document before each block execution.
16
+ #
17
+ # @example
18
+ # person = Person.find(existing.id)
19
+ # person.with_locking do
20
+ # person.update!(name: "Person 1 updated")
21
+ # end
22
+ #
23
+ # @param [ Integer ] max_retries The maximum number of times to retry
24
+ #
25
+ # @return [ Object ] The result of the block
26
+ def with_locking(max_retries: 3)
27
+ self.class.with_locking(max_retries: max_retries) do
28
+ reload
29
+ yield
30
+ end
31
+ end
32
+
33
+ module ClassMethods # :nodoc:
34
+ ##
35
+ # Retries the block of code a specified number of times when a
36
+ # Mongoid::StaleObjectError is raised.
37
+ #
38
+ # @example
39
+ # Person.with_locking do
40
+ # person = Person.find(existing.id)
41
+ # Person.update!(name: "Person 1 updated")
42
+ # end
43
+ #
44
+ # @param [ Integer ] max_retries The maximum number of times to retry
45
+ #
46
+ # @return [ Object ] The result of the block
47
+ def with_locking(max_retries: 3)
48
+ retries = 0
49
+
50
+ begin
51
+ yield
52
+ rescue Mongoid::StaleObjectError
53
+ retries += 1
54
+ raise if retries > max_retries
55
+
56
+ sleep Mongoid::Locking.backoff_algorithm(retries)
57
+ retry
58
+ end
59
+ end
60
+
61
+ def backoff_algorithm(retries)
62
+ (2 + rand)**retries
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Mongoid
4
4
  module Locking
5
- VERSION = "1.1.1"
5
+ VERSION = "1.3.0"
6
6
  end
7
7
  end
@@ -9,6 +9,7 @@ require_relative "locking/reloadable"
9
9
  require_relative "locking/persistable/creatable"
10
10
  require_relative "locking/persistable/updatable"
11
11
  require_relative "locking/persistable"
12
+ require_relative "locking/retry"
12
13
 
13
14
  module Mongoid
14
15
  ##
@@ -16,13 +17,19 @@ module Mongoid
16
17
  #
17
18
  # @since 0.1.0
18
19
  module Locking
19
- def self.included(base)
20
- base.field :lock_version, type: Integer
21
- base.before_create { self.lock_version = 0 }
20
+ class << self
21
+ def included(base)
22
+ base.field :lock_version, type: Integer
23
+ base.before_create { self.lock_version = 0 }
22
24
 
23
- base.include Mongoid::Locking::Selectable
24
- base.include Mongoid::Locking::Reloadable
25
- # base.include Mongoid::Locking::Reloadable if Mongoid::VERSION >= "7"
25
+ base.include Mongoid::Locking::Selectable
26
+ base.include Mongoid::Locking::Reloadable
27
+ base.include Mongoid::Locking::Retry
28
+ end
29
+
30
+ def backoff_algorithm(retries)
31
+ (2**retries) + rand
32
+ end
26
33
  end
27
34
  end
28
35
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mongoid-locking
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rodrigo RA
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-12-08 00:00:00.000000000 Z
11
+ date: 2024-03-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: mongoid
@@ -121,6 +121,7 @@ files:
121
121
  - lib/mongoid/locking/persistable/creatable.rb
122
122
  - lib/mongoid/locking/persistable/updatable.rb
123
123
  - lib/mongoid/locking/reloadable.rb
124
+ - lib/mongoid/locking/retry.rb
124
125
  - lib/mongoid/locking/selectable.rb
125
126
  - lib/mongoid/locking/version.rb
126
127
  - lib/mongoid/stale_object_error.rb