concurrent_rails 0.6.1 → 0.7.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 +4 -4
- data/README.md +25 -99
- data/lib/concurrent_rails/version.rb +1 -1
- metadata +9 -9
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: dc63638fb76dc139c10b9ed53d61e020f7288bbd13f3bed9c4e5129b93c016c9
|
|
4
|
+
data.tar.gz: e61aae19d4fd6f1023be007ed77a2edf333a922fe0ab522ea533175e975ce941
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f236962918393d4f1a8c81edec667564fc862825940a0ef30e33053d1d1061017172b14cb407eb87177ea1db41471de9c7abafb0648ebd63183b107df1ad3604
|
|
7
|
+
data.tar.gz: e453ea4cdcd0161d0159dfeaec1931e43dbd992120ecb57953935517d28072a79338a731feb1be660108dd24f3bb376f66f013de3c1653d08a5e4f756ecc0b7e
|
data/README.md
CHANGED
|
@@ -40,7 +40,7 @@ irb(main):002:0> future.value
|
|
|
40
40
|
|
|
41
41
|
### Delayed futures
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
A delayed future is a Future that is enqueued but not run until `#touch` or any other method that requires a resolution is called.
|
|
44
44
|
|
|
45
45
|
```ruby
|
|
46
46
|
irb(main):002:0> delay = ConcurrentRails::Promises.delay { 42 }
|
|
@@ -65,9 +65,9 @@ Three methods will trigger a resolution: `#touch`, `#value` and `#wait`: `#touch
|
|
|
65
65
|
|
|
66
66
|
Delayed and regular futures can set a callback to be executed after the resolution of the future. There are three different callbacks:
|
|
67
67
|
|
|
68
|
-
* `on_resolution`: runs after the
|
|
68
|
+
* `on_resolution`: runs after the Future is resolved and yields three parameters to the callback in the following order: `true/false` for future's fulfillment, `value` as the result of the future execution, and `reason`, that will be `nil` if the future fulfilled or the error that the future triggered.
|
|
69
69
|
|
|
70
|
-
* `on_fulfillment`: runs after the
|
|
70
|
+
* `on_fulfillment`: runs after the Future is fulfilled and yields `value` to the callback
|
|
71
71
|
|
|
72
72
|
* `on_rejection`: runs after the future is rejected and yields the `error` to the callback
|
|
73
73
|
|
|
@@ -81,101 +81,9 @@ delay.touch
|
|
|
81
81
|
|
|
82
82
|
All of these callbacks have a bang version (e.g. `on_fulfillment!`). The bang version will execute the callback on the same thread pool that was initially set up and the version without bang will run asynchronously on a different executor.
|
|
83
83
|
|
|
84
|
-
### (Deprecated) Future
|
|
85
|
-
|
|
86
|
-
`ConcurrentRails::Future` will execute your code in a separate thread and you can check the progress of it whenever you need it. When the task is ready, you can access the result with `#result` function:
|
|
87
|
-
|
|
88
|
-
```ruby
|
|
89
|
-
irb(main):001:0> future = ConcurrentRails::Future.new do
|
|
90
|
-
sleep(5) # Simulate a long running task
|
|
91
|
-
42
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
# at this point, nothing has happened yet.
|
|
95
|
-
|
|
96
|
-
irb(main):002:0> future.execute
|
|
97
|
-
|
|
98
|
-
irb(main):003:0> future.state
|
|
99
|
-
=> :processing
|
|
100
|
-
|
|
101
|
-
# after 5 seconds
|
|
102
|
-
irb(main):004:0> future.state
|
|
103
|
-
=> :fulfilled
|
|
104
|
-
|
|
105
|
-
irb(main):005:0> future.value
|
|
106
|
-
=> 42
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
A task can also fail. In this case, the state of the future will be `rejected` and the exception can be accessed by invoking `reason`
|
|
110
|
-
|
|
111
|
-
```ruby
|
|
112
|
-
irb(main):001:1* future = ConcurrentRails::Future.new do
|
|
113
|
-
irb(main):002:1* 2 / 0
|
|
114
|
-
irb(main):003:0> end.execute
|
|
115
|
-
|
|
116
|
-
=> #<ConcurrentRails::Future...
|
|
117
|
-
|
|
118
|
-
irb(main):004:0> future.state
|
|
119
|
-
=> :rejected
|
|
120
|
-
|
|
121
|
-
irb(main):005:0> future.reason
|
|
122
|
-
=> #<ZeroDivisionError: divided by 0>
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
### (Deprecated) Multi
|
|
126
|
-
|
|
127
|
-
`ConcurrentRails::Multi` will let you execute multiple tasks in parallel and aggregate the results of each task when they are done. `Multi` accepts an undefined number of `Proc`s.
|
|
128
|
-
|
|
129
|
-
```ruby
|
|
130
|
-
irb(main):001:1* multi = ConcurrentRails::Multi.enqueue(
|
|
131
|
-
irb(main):002:1* -> { 42 },
|
|
132
|
-
irb(main):003:1* -> { :multi_test }
|
|
133
|
-
irb(main):004:0> )
|
|
134
|
-
|
|
135
|
-
=> #<ConcurrentRails::Multi:0x00007fbc3f9ca3f8 @actions=[#<Proc:0x00007fbc3f9ca470..
|
|
136
|
-
irb(main):005:0> multi.complete?
|
|
137
|
-
=> true
|
|
138
|
-
|
|
139
|
-
irb(main):006:0> multi.compute
|
|
140
|
-
=> [42, :multi_test]
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
Given the fact that you can send any number of `Proc`s, the result from `compute` will always be an array, even if you provide only one proc.
|
|
144
|
-
|
|
145
|
-
```ruby
|
|
146
|
-
irb(main):007:1* multi = ConcurrentRails::Multi.enqueue(
|
|
147
|
-
irb(main):008:1* -> { 42 }
|
|
148
|
-
irb(main):009:0> )
|
|
149
|
-
=> #<ConcurrentRails::Multi:0x00007fbc403f0b98 @actions=[#<Proc:0x00007...
|
|
150
|
-
|
|
151
|
-
irb(main):010:0> multi.compute
|
|
152
|
-
=> [42]
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
Same as `Future`, one of the `Multi` tasks can fail. You can access the exception by calling `#errors`:
|
|
156
|
-
|
|
157
|
-
```ruby
|
|
158
|
-
irb(main):001:1* multi = ConcurrentRails::Multi.enqueue(
|
|
159
|
-
irb(main):002:1* -> { 42 },
|
|
160
|
-
irb(main):003:1* -> { 2 / 0 }
|
|
161
|
-
irb(main):004:0> )
|
|
162
|
-
=> #<ConcurrentRails::Multi:0x00007fb46d3ee3a0 @actions=[#<Proc:0x00007..
|
|
163
|
-
|
|
164
|
-
irb(main):005:0> multi.complete?
|
|
165
|
-
=> true
|
|
166
|
-
|
|
167
|
-
irb(main):006:0> multi.compute
|
|
168
|
-
=> [42, nil]
|
|
169
|
-
|
|
170
|
-
irb(main):007:0> multi.errors
|
|
171
|
-
=> [#<ZeroDivisionError: divided by 0>]
|
|
172
|
-
```
|
|
173
|
-
|
|
174
|
-
It is worth mention that a failed proc will return `nil`.
|
|
175
|
-
|
|
176
84
|
## Testing
|
|
177
85
|
|
|
178
|
-
If you are using RSpec, you will notice that it might not play well with threads. ActiveRecord opens a database connection for every thread and since RSpec tests are wrapped in a transaction, by the time your promise tries to access something on the database, for example, a user, gems like Database Cleaner probably already triggered and deleted the user, resulting in `ActiveRecord::RecordNotFound` errors. You have a couple of solutions like
|
|
86
|
+
If you are using RSpec, you will notice that it might not play well with threads. ActiveRecord opens a database connection for every thread and since RSpec tests are wrapped in a transaction, by the time your promise tries to access something on the database, for example, a user, gems like Database Cleaner probably already triggered and deleted the user, resulting in `ActiveRecord::RecordNotFound` errors. You have a couple of solutions like disabling transactional fixtures if you are using it or update the Database Cleaner strategy (that will result in much slower tests).
|
|
179
87
|
Since none of these solutions were satisfactory to me, I created `ConcurrentRails::Testing` with two strategies: `immediate` and `fake`. When you wrap a Promise's `future` with `immediate`, the executor gets replaced from `:io` to `:immediate`. It still returns a promise anyway. This is not the case with `fake` strategy: it executes the task outside the `ConcurrentRails` engine and returns whatever `.value` would return:
|
|
180
88
|
|
|
181
89
|
`immediate` strategy:
|
|
@@ -204,11 +112,11 @@ irb(main):004:0> result.class
|
|
|
204
112
|
=> Integer
|
|
205
113
|
```
|
|
206
114
|
|
|
207
|
-
You can also set the
|
|
115
|
+
You can also set the strategy globally using `ConcurrentRails::Testing.fake!` or `ConcurrentRails::Testing.immediate!`
|
|
208
116
|
|
|
209
117
|
## Further reading
|
|
210
118
|
|
|
211
|
-
For more information on how Futures
|
|
119
|
+
For more information on how Futures works and how Rails handles multithread check these links:
|
|
212
120
|
|
|
213
121
|
[Future documentation](https://github.com/ruby-concurrency/concurrent-ruby/blob/master/docs-source/future.md)
|
|
214
122
|
|
|
@@ -236,7 +144,25 @@ gem install concurrent_rails
|
|
|
236
144
|
|
|
237
145
|
## Contributing
|
|
238
146
|
|
|
239
|
-
Pull
|
|
147
|
+
Pull requests are always welcome
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
## Updating Ruby or Rails versions using Appraisal
|
|
151
|
+
|
|
152
|
+
This gem uses Appraisal for multiple Ruby and Rails versions testing. To update the Ruby or Rails versions, you can run:
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
bundle exec appraisal install
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
and to run the tests for all versions, you can run:
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
bundle exec appraisal rake test
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Check the [usage](https://github.com/thoughtbot/appraisal?tab=readme-ov-file#usage) section of the Appraisal gem for more information on how to use it.
|
|
165
|
+
|
|
240
166
|
|
|
241
167
|
## License
|
|
242
168
|
|
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: concurrent_rails
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.7.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Luiz Eduardo Kowalski
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: railties
|
|
@@ -16,14 +15,14 @@ dependencies:
|
|
|
16
15
|
requirements:
|
|
17
16
|
- - ">="
|
|
18
17
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: '
|
|
18
|
+
version: '7.2'
|
|
20
19
|
type: :runtime
|
|
21
20
|
prerelease: false
|
|
22
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
22
|
requirements:
|
|
24
23
|
- - ">="
|
|
25
24
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: '
|
|
25
|
+
version: '7.2'
|
|
27
26
|
- !ruby/object:Gem::Dependency
|
|
28
27
|
name: zeitwerk
|
|
29
28
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -59,8 +58,10 @@ homepage: https://github.com/luizkowalski/concurrent_rails
|
|
|
59
58
|
licenses:
|
|
60
59
|
- MIT
|
|
61
60
|
metadata:
|
|
61
|
+
homepage_uri: https://github.com/luizkowalski/concurrent_rails
|
|
62
|
+
source_code_uri: https://github.com/luizkowalski/concurrent_rails
|
|
63
|
+
changelog_uri: https://github.com/luizkowalski/concurrent_rails/blob/master/CHANGELOG.md
|
|
62
64
|
rubygems_mfa_required: 'true'
|
|
63
|
-
post_install_message:
|
|
64
65
|
rdoc_options: []
|
|
65
66
|
require_paths:
|
|
66
67
|
- lib
|
|
@@ -68,15 +69,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
68
69
|
requirements:
|
|
69
70
|
- - ">="
|
|
70
71
|
- !ruby/object:Gem::Version
|
|
71
|
-
version: '3.
|
|
72
|
+
version: '3.2'
|
|
72
73
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
73
74
|
requirements:
|
|
74
75
|
- - ">="
|
|
75
76
|
- !ruby/object:Gem::Version
|
|
76
77
|
version: '0'
|
|
77
78
|
requirements: []
|
|
78
|
-
rubygems_version:
|
|
79
|
-
signing_key:
|
|
79
|
+
rubygems_version: 4.0.2
|
|
80
80
|
specification_version: 4
|
|
81
81
|
summary: Multithread is hard
|
|
82
82
|
test_files: []
|