lockless 0.1.0 → 0.2.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 +4 -4
- data/.github/workflows/main.yml +1 -1
- data/.rspec_status +7 -5
- data/README.md +40 -5
- data/lib/lockless/model.rb +17 -4
- data/lib/lockless/relation.rb +24 -0
- data/lib/lockless/version.rb +1 -1
- data/lib/lockless.rb +1 -0
- data/lockless.gemspec +1 -8
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dfbf0af85c74d0fc3b05165382effa2efcc35648ba4d6fa0c378a9d7bb2b53ff
|
4
|
+
data.tar.gz: ab37c259d96aeb2689756b84748be5648ced851c374db572ff6d215fb17167c6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 99c8c3878e4353f8a8863dc0d4efb0b24a63ac6460fdf438274a225404cf0a106af52e677cf3869fced18aa736c9a0d3ad9afdee5fa97ae6219fb3cd54861360
|
7
|
+
data.tar.gz: c06923cbc493206e78bf8c5376a3b3ba21c2d7265ed461d2018fe50ca1358e2d1af66def2d03e015cd1a97ad032f4f944bacb15d6391e7ed60d7b97212948b1a
|
data/.github/workflows/main.yml
CHANGED
data/.rspec_status
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
example_id | status | run_time |
|
2
2
|
------------------------------------------ | ------ | --------------- |
|
3
|
-
./spec/lockless/model_spec.rb[1:1] | passed | 0.
|
4
|
-
./spec/lockless/model_spec.rb[1:2:1:1] | passed | 0.
|
5
|
-
./spec/lockless/model_spec.rb[1:
|
6
|
-
./spec/lockless/model_spec.rb[1:
|
7
|
-
./spec/lockless/model_spec.rb[1:
|
3
|
+
./spec/lockless/model_spec.rb[1:1] | passed | 0.01148 seconds |
|
4
|
+
./spec/lockless/model_spec.rb[1:2:1:1] | passed | 0.02232 seconds |
|
5
|
+
./spec/lockless/model_spec.rb[1:3:1:1] | passed | 0.00415 seconds |
|
6
|
+
./spec/lockless/model_spec.rb[1:3:2:1:1] | passed | 0.005 seconds |
|
7
|
+
./spec/lockless/model_spec.rb[1:3:2:1:2:1] | passed | 0.03027 seconds |
|
8
|
+
./spec/lockless/model_spec.rb[1:3:2:2:1] | passed | 0.00563 seconds |
|
9
|
+
./spec/lockless/model_spec.rb[1:3:2:2:2] | passed | 0.00243 seconds |
|
data/README.md
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# Lockless
|
2
2
|
|
3
|
-
|
3
|
+
[](https://badge.fury.io/rb/lockless)
|
4
|
+
[](https://github.com/cianmce/lockless/actions)
|
4
5
|
|
5
6
|
Allows for safe concurrent updates to a single record without the need for locks.
|
6
7
|
|
@@ -54,6 +55,15 @@ class AddLocklessUuidToUsers < ActiveRecord::Migration[5.0]
|
|
54
55
|
end
|
55
56
|
```
|
56
57
|
|
58
|
+
### Custom column name
|
59
|
+
|
60
|
+
```ruby
|
61
|
+
class User < ActiveRecord::Base
|
62
|
+
include Lockless::Model
|
63
|
+
self.lockless_column = :custom_uuid
|
64
|
+
end
|
65
|
+
```
|
66
|
+
|
57
67
|
### Sample Usage
|
58
68
|
|
59
69
|
```ruby
|
@@ -66,15 +76,28 @@ user2.name = "new name2"
|
|
66
76
|
|
67
77
|
# Save user2 before saving user1
|
68
78
|
user2.lockless_save! # => true
|
79
|
+
|
80
|
+
# user1 fails to save as it's been updated earlier by user2
|
69
81
|
user1.lockless_save! # => false
|
70
82
|
|
71
|
-
# when lockless_save doesn't work you can either
|
83
|
+
# when lockless_save doesn't work you can either ignore the update and continue
|
72
84
|
# or reload the record and try again
|
85
|
+
# or calling `.save!` will forcefully save changes without respecting lockless
|
86
|
+
# `.save!` will cause the UUID to be changed again
|
73
87
|
|
74
88
|
user1.reload
|
75
89
|
user1.name # => "new name2"
|
76
90
|
user1.name = "new name3"
|
77
91
|
user1.lockless_save! # => true
|
92
|
+
user1.reload.name # => "new name3"
|
93
|
+
|
94
|
+
user1.reload
|
95
|
+
user1.name = "new name1"
|
96
|
+
User.all.update_all(name: "new name4")
|
97
|
+
|
98
|
+
# user1 fails to save as it's been updated earlier in `update_all`
|
99
|
+
user1.lockless_save! # => false
|
100
|
+
user1.reload.name # => "new name4"
|
78
101
|
```
|
79
102
|
|
80
103
|
## Development
|
@@ -83,13 +106,26 @@ After checking out the repo, run `bin/setup` to install dependencies. Then, run
|
|
83
106
|
|
84
107
|
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
85
108
|
|
86
|
-
###
|
109
|
+
### Testing
|
110
|
+
#### Run all specs + standardrb
|
87
111
|
|
88
112
|
```sh
|
89
113
|
bundle exec rake
|
90
114
|
```
|
91
115
|
|
92
|
-
|
116
|
+
#### Run only standardrb
|
117
|
+
|
118
|
+
```sh
|
119
|
+
bundle exec rake standard
|
120
|
+
````
|
121
|
+
|
122
|
+
#### Apply standardrb auto fixes
|
123
|
+
|
124
|
+
```sh
|
125
|
+
bundle exec rake standard:fix
|
126
|
+
```
|
127
|
+
|
128
|
+
#### Run specs using guard
|
93
129
|
|
94
130
|
```sh
|
95
131
|
bundle exec guard
|
@@ -111,6 +147,5 @@ Everyone interacting in the Lockless project's codebases, issue trackers, chat r
|
|
111
147
|
|
112
148
|
## TODO
|
113
149
|
|
114
|
-
- [ ] Allow for custom lockless column name
|
115
150
|
- [ ] Allow for custom primary key column name
|
116
151
|
- [ ] Allow a boolean to be passed to allow for validation to be skipped like in `.save`
|
data/lib/lockless/model.rb
CHANGED
@@ -2,6 +2,11 @@
|
|
2
2
|
|
3
3
|
module Lockless
|
4
4
|
# Handles lockless saves
|
5
|
+
|
6
|
+
#
|
7
|
+
# Options:
|
8
|
+
#
|
9
|
+
# - :lockless_column - The columns used to track soft delete, defaults to `:lockless_uuid`.
|
5
10
|
module Model
|
6
11
|
extend ActiveSupport::Concern
|
7
12
|
|
@@ -10,6 +15,14 @@ module Lockless
|
|
10
15
|
self.lockless_column = :lockless_uuid
|
11
16
|
|
12
17
|
before_save :set_lockless_uuid
|
18
|
+
|
19
|
+
delegate :generate_uuid, :lockless_column, to: :class
|
20
|
+
end
|
21
|
+
|
22
|
+
class_methods do
|
23
|
+
def generate_uuid
|
24
|
+
SecureRandom.uuid
|
25
|
+
end
|
13
26
|
end
|
14
27
|
|
15
28
|
# Saves record if it has not be modified in the time after it was loaded from the database.
|
@@ -18,18 +31,18 @@ module Lockless
|
|
18
31
|
# false is returned if record is outdated or invalid
|
19
32
|
def lockless_save
|
20
33
|
return false unless valid?
|
21
|
-
old_lockless_uuid =
|
34
|
+
old_lockless_uuid = public_send(lockless_column)
|
22
35
|
return save if new_record?
|
23
36
|
|
24
37
|
run_callbacks(:save) do |variable|
|
25
38
|
new_attrs = changed.collect { |prop| [prop.to_sym, self[prop]] }.to_h
|
26
39
|
|
27
|
-
update_count = self.class.where(id
|
40
|
+
update_count = self.class.where(:id => id, lockless_column => old_lockless_uuid).update_all(new_attrs)
|
28
41
|
if update_count == 1
|
29
42
|
changes_applied
|
30
43
|
true
|
31
44
|
else
|
32
|
-
|
45
|
+
public_send("#{lockless_column}=", old_lockless_uuid)
|
33
46
|
false
|
34
47
|
end
|
35
48
|
end
|
@@ -49,7 +62,7 @@ module Lockless
|
|
49
62
|
private
|
50
63
|
|
51
64
|
def set_lockless_uuid
|
52
|
-
|
65
|
+
public_send("#{lockless_column}=", generate_uuid)
|
53
66
|
end
|
54
67
|
end
|
55
68
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Lockless
|
4
|
+
module Relation
|
5
|
+
# Appends update attributes with a random UUID when the model is a lockless model
|
6
|
+
#
|
7
|
+
# @param [String, Array, Hash] A string, array, or hash representing the SET part of an SQL statement.
|
8
|
+
# Lockless will only append random UUID updates if the param is a Hash
|
9
|
+
#
|
10
|
+
# @return [Boolean] Similar to `.save`, true is returned if the model is updated
|
11
|
+
# false is returned if record is outdated or invalid
|
12
|
+
def update_all(updates)
|
13
|
+
if updates.is_a?(Hash)
|
14
|
+
if model.method_defined?(:lockless_column) && updates[model.lockless_column].blank?
|
15
|
+
updates[model.lockless_column] = model.generate_uuid
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
super(updates)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
ActiveRecord::Relation.prepend(Lockless::Relation)
|
data/lib/lockless/version.rb
CHANGED
data/lib/lockless.rb
CHANGED
data/lockless.gemspec
CHANGED
@@ -14,10 +14,9 @@ Gem::Specification.new do |spec|
|
|
14
14
|
spec.license = "MIT"
|
15
15
|
spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
|
16
16
|
|
17
|
-
# spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
|
18
|
-
|
19
17
|
spec.metadata["homepage_uri"] = spec.homepage
|
20
18
|
spec.metadata["source_code_uri"] = spec.homepage
|
19
|
+
spec.metadata["documentation_uri"] = "https://www.rubydoc.info/gems/lockless/#{Lockless::VERSION}"
|
21
20
|
|
22
21
|
# Specify which files should be added to the gem when it is released.
|
23
22
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
@@ -28,12 +27,6 @@ Gem::Specification.new do |spec|
|
|
28
27
|
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
29
28
|
spec.require_paths = ["lib"]
|
30
29
|
|
31
|
-
# Uncomment to register a new dependency of your gem
|
32
|
-
# spec.add_dependency "example-gem", "~> 1.0"
|
33
|
-
|
34
|
-
# For more information and examples about making a new gem, checkout our
|
35
|
-
# guide at: https://bundler.io/guides/creating_gem.html
|
36
|
-
|
37
30
|
spec.add_development_dependency "standardrb", "~> 1.0"
|
38
31
|
spec.add_development_dependency "bundler", ">= 1"
|
39
32
|
spec.add_development_dependency "rake", "~> 13.0"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lockless
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Cian McElhinney
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-11-
|
11
|
+
date: 2021-11-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: standardrb
|
@@ -215,6 +215,7 @@ files:
|
|
215
215
|
- bin/setup
|
216
216
|
- lib/lockless.rb
|
217
217
|
- lib/lockless/model.rb
|
218
|
+
- lib/lockless/relation.rb
|
218
219
|
- lib/lockless/version.rb
|
219
220
|
- lockless.gemspec
|
220
221
|
homepage: https://github.com/cianmce/lockless
|
@@ -223,6 +224,7 @@ licenses:
|
|
223
224
|
metadata:
|
224
225
|
homepage_uri: https://github.com/cianmce/lockless
|
225
226
|
source_code_uri: https://github.com/cianmce/lockless
|
227
|
+
documentation_uri: https://www.rubydoc.info/gems/lockless/0.2.2
|
226
228
|
post_install_message:
|
227
229
|
rdoc_options: []
|
228
230
|
require_paths:
|