lockless 0.1.1 → 0.2.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 +4 -4
- data/.github/workflows/main.yml +1 -1
- data/.rspec_status +7 -5
- data/README.md +28 -4
- data/lib/lockless/model.rb +17 -4
- data/lib/lockless/relation.rb +15 -0
- data/lib/lockless/version.rb +1 -1
- data/lib/lockless.rb +1 -0
- data/lockless.gemspec +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b5cc6bb9658c48705098956a9caabc7c3f10cd99631dcd6b0fe776592f57cc9d
|
4
|
+
data.tar.gz: 48d0dc5d8709224b6481e6d5b6316487c66b82b735a88a77d370d783bcaa62fd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a6580bfc5ac693248c355b82d6e6b9113c2772660ce0d7b2d9c31553a2da212d122cf0b757c93df78b7a985544f030b6bb66697e400f8857a749418a17ca16c0
|
7
|
+
data.tar.gz: 3a6da4c0530cbe62a28831aec86966953cecf54433fac6ccd75070ff659b4febb1db2b1b5232f521476fca6595fc0a36402bcf4e3a42be9ba980e578e2e4cfb7
|
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
|
|
@@ -66,6 +67,8 @@ user2.name = "new name2"
|
|
66
67
|
|
67
68
|
# Save user2 before saving user1
|
68
69
|
user2.lockless_save! # => true
|
70
|
+
|
71
|
+
# user1 fails to save as it's been updated earlier by user2
|
69
72
|
user1.lockless_save! # => false
|
70
73
|
|
71
74
|
# when lockless_save doesn't work you can either ignored the update
|
@@ -75,6 +78,15 @@ user1.reload
|
|
75
78
|
user1.name # => "new name2"
|
76
79
|
user1.name = "new name3"
|
77
80
|
user1.lockless_save! # => true
|
81
|
+
user1.reload.name # => "new name3"
|
82
|
+
|
83
|
+
user1.reload
|
84
|
+
user1.name = "new name1"
|
85
|
+
User.all.update_all(name: "new name4")
|
86
|
+
|
87
|
+
# user1 fails to save as it's been updated earlier in `update_all`
|
88
|
+
user1.lockless_save! # => false
|
89
|
+
user1.reload.name # => "new name4"
|
78
90
|
```
|
79
91
|
|
80
92
|
## Development
|
@@ -83,13 +95,26 @@ After checking out the repo, run `bin/setup` to install dependencies. Then, run
|
|
83
95
|
|
84
96
|
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
97
|
|
86
|
-
###
|
98
|
+
### Testing
|
99
|
+
#### Run all specs + standardrb
|
87
100
|
|
88
101
|
```sh
|
89
102
|
bundle exec rake
|
90
103
|
```
|
91
104
|
|
92
|
-
|
105
|
+
#### Run only standardrb
|
106
|
+
|
107
|
+
```sh
|
108
|
+
bundle exec rake standard
|
109
|
+
````
|
110
|
+
|
111
|
+
#### Apply standardrb auto fixes
|
112
|
+
|
113
|
+
```sh
|
114
|
+
bundle exec rake standard:fix
|
115
|
+
```
|
116
|
+
|
117
|
+
#### Run specs using guard
|
93
118
|
|
94
119
|
```sh
|
95
120
|
bundle exec guard
|
@@ -111,6 +136,5 @@ Everyone interacting in the Lockless project's codebases, issue trackers, chat r
|
|
111
136
|
|
112
137
|
## TODO
|
113
138
|
|
114
|
-
- [ ] Allow for custom lockless column name
|
115
139
|
- [ ] Allow for custom primary key column name
|
116
140
|
- [ ] 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,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Lockless
|
4
|
+
module Relation
|
5
|
+
def update_all(updates)
|
6
|
+
if model.method_defined?(:lockless_column) && updates[model.lockless_column].blank?
|
7
|
+
updates[model.lockless_column] = model.generate_uuid
|
8
|
+
end
|
9
|
+
|
10
|
+
super(updates)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
ActiveRecord::Relation.prepend(Lockless::Relation)
|
data/lib/lockless/version.rb
CHANGED
data/lib/lockless.rb
CHANGED
data/lockless.gemspec
CHANGED
@@ -12,7 +12,7 @@ Gem::Specification.new do |spec|
|
|
12
12
|
spec.description = "Allows for safe concurrent updates to a single record without the need for locks."
|
13
13
|
spec.homepage = "https://github.com/cianmce/lockless"
|
14
14
|
spec.license = "MIT"
|
15
|
-
spec.required_ruby_version = ">= 2.3.0"
|
15
|
+
spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
|
16
16
|
|
17
17
|
spec.metadata["homepage_uri"] = spec.homepage
|
18
18
|
spec.metadata["source_code_uri"] = spec.homepage
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lockless
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Cian McElhinney
|
@@ -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
|