yes-core 2.8.0 → 2.8.1

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: 6d24f90d38a691d222088d441c0f29e661e22645a3dcaf58a642640a0b3e760b
4
- data.tar.gz: 6ced607e8d0c551dd77957371cf96e3bc2b321b2d3f26291b764667f70c9e003
3
+ metadata.gz: 556abdbb646c43a30ef36450a66d72b26b702ab1041bfce62701cbec40e72e26
4
+ data.tar.gz: 15c8870cbb4b60e6b8097479386aa99a6d113fae5950b1d907821e78063f40c4
5
5
  SHA512:
6
- metadata.gz: 831023659ee9a669990126263ef7132a71a5a1302a39fc5e0d0be38c08566a8d64d4dd60b90a7792b92d3050702f5fb445c5927aafa70fa86f64301cc7a93b63
7
- data.tar.gz: 13968b17bb7fe5dd7d088dcea38105f0ea156e7fe34edbf1b60a6322d3d6ba55f9954074585c6cdccc02d92fd4a4a7d80cdb183e23d46435d6012c8c2f55cef0
6
+ metadata.gz: 260dc63645964b5a0efa9e2da46e0c7e00b5aed59f13393e80db402f889e73246818573e368038afbea1318d264a6a40a73a68108dcd2c24b14f97531200a3c1
7
+ data.tar.gz: bf0561d95288742beb14f5cfd8fe724c7abb52c727a3de97c579171a6fffd3c4f594e4c29d204e4f99a140499ff7dec6acf2aaabb3db5895a85ca5f985fb578f
data/CHANGELOG.md CHANGED
@@ -1,5 +1,8 @@
1
1
  # Changelog
2
2
 
3
+ ## [2.8.1] - 2026-10-30
4
+ - See root CHANGELOG.md for details.
5
+
3
6
  ## [2.8.0] - 2026-10-29
4
7
  - See root CHANGELOG.md for details.
5
8
 
@@ -8,6 +8,9 @@ module Yes
8
8
  # Ensures that read model revisions match expected event revisions
9
9
  # Uses ExponentialRetrier for retry logic with exponential backoff
10
10
  # This handles eventual consistency between the event store and read model databases
11
+ #
12
+ # Waiting for the expected revision happens without a lock. Only the final check and the block run
13
+ # while the read model row is locked, see {#call}.
11
14
  class ReadModelRevisionGuard
12
15
  # Error raised when revision guard fails after maximum retries
13
16
  class RevisionMismatchError < Yes::Core::Error; end
@@ -96,7 +99,11 @@ module Yes
96
99
 
97
100
  # Executes the guard logic with retry mechanism
98
101
  #
99
- # @yield Block to execute when revisions match
102
+ # Once the revision matches, the row is locked, reloaded and checked again before the block runs. Without
103
+ # that, two writers applying the same event (for example a slow command and a concurrent recovery) can both
104
+ # pass the check; the later one then writes its stale copy over the newer state and resets the revision.
105
+ #
106
+ # @yield Block to execute when revisions match, runs while the read model row is locked
100
107
  # @return [Object] Result of the block execution
101
108
  # @raise [RevisionMismatchError] When revisions don't match after maximum retries
102
109
  # @raise [TimeoutError] When timeout is exceeded
@@ -108,9 +115,8 @@ module Yes
108
115
  retrier.call(
109
116
  condition_check: -> { check_revision_and_return_match_status },
110
117
  failure_message: revision_mismatch_message,
111
- timeout_message: timeout_message,
112
- &
113
- )
118
+ timeout_message: timeout_message
119
+ ) { with_row_lock(&) }
114
120
  rescue Yes::Core::Utils::ExponentialRetrier::RetryFailedError => e
115
121
  raise RevisionMismatchError, e.message
116
122
  rescue Yes::Core::Utils::ExponentialRetrier::TimeoutError => e
@@ -122,6 +128,22 @@ module Yes
122
128
 
123
129
  attr_reader :read_model, :revision_column
124
130
 
131
+ # Locks and reloads the read model row, re-checks the revision and yields.
132
+ # The lock is held until the surrounding transaction (if any) ends.
133
+ #
134
+ # @yield Block to execute while the row is locked
135
+ # @return [Object] Result of the block execution
136
+ # @raise [RevisionAlreadyAppliedError] When another writer applied the revision in the meantime
137
+ # @raise [RevisionMismatchError] When the locked row is behind the expected revision
138
+ def with_row_lock
139
+ read_model.with_lock do
140
+ check_revision_status!
141
+ raise RevisionMismatchError, revision_mismatch_message unless revision_matches?
142
+
143
+ yield
144
+ end
145
+ end
146
+
125
147
  # Creates the retrier with custom logging context
126
148
  #
127
149
  # @return [Yes::Core::Utils::ExponentialRetrier] Configured retrier instance
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Yes
4
4
  module Core
5
- VERSION = '2.8.0'
5
+ VERSION = '2.8.1'
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: yes-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.8.0
4
+ version: 2.8.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nico Ritsche