lockless 0.1.0 → 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Gem Version](https://badge.fury.io/rb/lockless.png)](https://badge.fury.io/rb/lockless)
|
4
|
+
[![CI Status](https://github.com/cianmce/lockless/actions/workflows/main.yml/badge.svg)](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:
|