periodic_job_mongoid 0.1.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 +7 -0
- data/.rspec +3 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +19 -0
- data/Gemfile.lock +70 -0
- data/LICENSE.md +22 -0
- data/README.md +272 -0
- data/Rakefile +8 -0
- data/lib/periodic_job_mongoid/checkpoint.rb +39 -0
- data/lib/periodic_job_mongoid/job.rb +71 -0
- data/lib/periodic_job_mongoid/scheduler.rb +31 -0
- data/lib/periodic_job_mongoid/version.rb +5 -0
- data/lib/periodic_job_mongoid.rb +8 -0
- data/mongoid.yml +6 -0
- data/periodic_job_mongoid.gemspec +35 -0
- metadata +81 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 9c5b281d510336a50b949ceb174dbb388041a15c6467f7abe1cb9c8e32efb200
|
4
|
+
data.tar.gz: fd32ff0b4c6c05c8645fbc018800cdb59779359dd75b7636a9f300903fc1d773
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f7c58cce1f73add0b97b2ab0fe982010014bc92bd7172e4e41f64b9f0bbaa36db0e911e39dfef15985aa2731df49f6395309177c51aa3ddab6aebc16e2d82220
|
7
|
+
data.tar.gz: 696704a8f8f45d11c3042c901c590ad1d4085c1a627196e3a6e1166f61f168ab124aac0796efbc0e0161df1bc7ef8b3c9565ff132112ccac3a03070280f8f461
|
data/.rspec
ADDED
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
3.1.2
|
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
# Specify your gem's dependencies in periodic_job_mongoid.gemspec
|
6
|
+
gemspec
|
7
|
+
|
8
|
+
# Rake is a Make-like program implemented in Ruby. Tasks and dependencies are specified in standard Ruby syntax. Rake has the following features: * Rakefiles (rake's version of Makefiles) are completely defined in standard Ruby syntax. No XML files to edit. No quirky Makefile syntax to worry about (is that a tab or a space?) * Users can specify tasks with prerequisites. * Rake supports rule patterns to synthesize implicit tasks. * Flexible FileLists that act like arrays but know about manipulating file names and paths. * Supports parallel execution of tasks.
|
9
|
+
gem "rake", "~> 13.0"
|
10
|
+
|
11
|
+
# BDD for Ruby
|
12
|
+
gem "rspec", "~> 3.12"
|
13
|
+
|
14
|
+
# A gem providing "time travel" and "time freezing" capabilities, making it dead simple to test time-dependent code. It provides a unified method to mock Time.now, Date.today, and DateTime.now in a single call.
|
15
|
+
gem 'timecop', "~> 0.9"
|
16
|
+
|
17
|
+
# The mongoid-rspec library provides a collection of RSpec-compatible matchers that help to test Mongoid documents.
|
18
|
+
gem 'mongoid-rspec', "~> 4.1.0"
|
19
|
+
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
periodic_job_mongoid (0.1.1)
|
5
|
+
mongoid (~> 7.4)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
activemodel (7.0.4)
|
11
|
+
activesupport (= 7.0.4)
|
12
|
+
activesupport (7.0.4)
|
13
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
14
|
+
i18n (>= 1.6, < 2)
|
15
|
+
minitest (>= 5.1)
|
16
|
+
tzinfo (~> 2.0)
|
17
|
+
bson (4.15.0)
|
18
|
+
concurrent-ruby (1.1.10)
|
19
|
+
diff-lcs (1.5.0)
|
20
|
+
i18n (1.12.0)
|
21
|
+
concurrent-ruby (~> 1.0)
|
22
|
+
minitest (5.16.3)
|
23
|
+
mongo (2.18.1)
|
24
|
+
bson (>= 4.14.1, < 5.0.0)
|
25
|
+
mongoid (7.5.2)
|
26
|
+
activemodel (>= 5.1, < 7.1, != 7.0.0)
|
27
|
+
mongo (>= 2.10.5, < 3.0.0)
|
28
|
+
ruby2_keywords (~> 0.0.5)
|
29
|
+
mongoid-compatibility (0.6.0)
|
30
|
+
activesupport
|
31
|
+
mongoid (>= 2.0)
|
32
|
+
mongoid-rspec (4.1.0)
|
33
|
+
activesupport (>= 3.0.0)
|
34
|
+
mongoid (>= 3.1)
|
35
|
+
mongoid-compatibility (>= 0.5.1)
|
36
|
+
rspec-core (~> 3.3)
|
37
|
+
rspec-expectations (~> 3.3)
|
38
|
+
rspec-mocks (~> 3.3)
|
39
|
+
rake (13.0.6)
|
40
|
+
rspec (3.12.0)
|
41
|
+
rspec-core (~> 3.12.0)
|
42
|
+
rspec-expectations (~> 3.12.0)
|
43
|
+
rspec-mocks (~> 3.12.0)
|
44
|
+
rspec-core (3.12.0)
|
45
|
+
rspec-support (~> 3.12.0)
|
46
|
+
rspec-expectations (3.12.0)
|
47
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
48
|
+
rspec-support (~> 3.12.0)
|
49
|
+
rspec-mocks (3.12.0)
|
50
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
51
|
+
rspec-support (~> 3.12.0)
|
52
|
+
rspec-support (3.12.0)
|
53
|
+
ruby2_keywords (0.0.5)
|
54
|
+
timecop (0.9.6)
|
55
|
+
tzinfo (2.0.5)
|
56
|
+
concurrent-ruby (~> 1.0)
|
57
|
+
|
58
|
+
PLATFORMS
|
59
|
+
x86_64-darwin-21
|
60
|
+
x86_64-linux
|
61
|
+
|
62
|
+
DEPENDENCIES
|
63
|
+
mongoid-rspec (~> 4.1.0)
|
64
|
+
periodic_job_mongoid!
|
65
|
+
rake (~> 13.0)
|
66
|
+
rspec (~> 3.12)
|
67
|
+
timecop (~> 0.9)
|
68
|
+
|
69
|
+
BUNDLED WITH
|
70
|
+
2.3.26
|
data/LICENSE.md
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
|
2
|
+
The MIT License (MIT)
|
3
|
+
|
4
|
+
Copyright (c) 2022 Yurie Nagorny
|
5
|
+
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
8
|
+
in the Software without restriction, including without limitation the rights
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
11
|
+
furnished to do so, subject to the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
14
|
+
copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
22
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,272 @@
|
|
1
|
+
# Periodic Job for Mongoid
|
2
|
+
|
3
|
+
Periodic Job executes jobs with a specified interval between runs. If you have several application servers, they will coordinate their work so that only one of the servers will run a job when the time comes to run the job. The coordination is done with help of the collection **periodic_job_checkpoints** in MongoDB database.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Install the gem and add to the application's Gemfile by executing:
|
8
|
+
|
9
|
+
$ bundle add periodic_job_mongoid
|
10
|
+
|
11
|
+
If bundler is not being used to manage dependencies, install the gem by executing:
|
12
|
+
|
13
|
+
$ gem install periodic_job_mongoid
|
14
|
+
|
15
|
+
## Usage
|
16
|
+
|
17
|
+
### Basic Usage
|
18
|
+
|
19
|
+
Create an instance of **PeriodicJob::Scheduler**, define jobs, and then run scheduler's **#tick** method periodically:
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
require 'periodic_job_mongoid'
|
23
|
+
|
24
|
+
scheduler = PeriodicJob::Scheduler.new
|
25
|
+
|
26
|
+
scheduler.every 30.seconds, :foo do
|
27
|
+
Foo.work
|
28
|
+
end
|
29
|
+
|
30
|
+
scheduler.every 1.minute, :bar do
|
31
|
+
Bar.work
|
32
|
+
end
|
33
|
+
|
34
|
+
loop do
|
35
|
+
sleep 1.second
|
36
|
+
scheduler.tick
|
37
|
+
end
|
38
|
+
```
|
39
|
+
|
40
|
+
In the above example when **scheduler#tick** is called, **Foo#work** will be executed after the initial 30 seconds pass, and then it will be called again 30 seconds **_after_** the point in time when **Foo#work** completes. **Bar#work** will be executed similarly, but with 1 minute gaps.
|
41
|
+
|
42
|
+
The first argument to **#every** is how much time should pass between block executions. So if **Foo#work** takes 10 seconds to complete, effectively it will be called every 40 seconds.
|
43
|
+
|
44
|
+
The second argument to **#every** (**:foo** and **:bar** above) are job IDs. If you have several instances of Periodic Job schedulers running, only one block passed to **#every** will be executed for a given job ID when the time comes for the job to run. This will be true regardless of whether the Priodic Job schedulers run in the same or different processes, and on the same or different machines as long as they share the same MongoDB database.
|
45
|
+
|
46
|
+
Only one job will be executed at a time per process regardless of job ID. If a job is due to run but another one is running, it will be delayed until the previous job completes. Having multiple processes with Periodic Job (for example if you have several application servers) will allow running jobs with differed IDs in parallel, but still only one per process.
|
47
|
+
|
48
|
+
Obviously the above example is not production ready (who wants infinite loops in their code). Later on you will find a more realistic example.
|
49
|
+
|
50
|
+
### Before and After hooks
|
51
|
+
|
52
|
+
You may define hooks that are executed before or after each job run:
|
53
|
+
|
54
|
+
```ruby
|
55
|
+
scheduler.before do |job_id|
|
56
|
+
puts "Before #{job_id}"
|
57
|
+
end
|
58
|
+
|
59
|
+
scheduler.before do |job_id|
|
60
|
+
puts "After #{job_id}"
|
61
|
+
end
|
62
|
+
```
|
63
|
+
|
64
|
+
Before and after hooks should be defined before you make calls to **#every**.
|
65
|
+
|
66
|
+
You can have only one before and one after hook per scheduler.
|
67
|
+
|
68
|
+
### Common error handler
|
69
|
+
|
70
|
+
If a job raises an exception, you may intercept it in a shared error handler:
|
71
|
+
|
72
|
+
```ruby
|
73
|
+
scheduler.error_handler do |e, job_id|
|
74
|
+
Rails.logger.error "Error in job #{job_id}: #{e.message}"
|
75
|
+
end
|
76
|
+
```
|
77
|
+
|
78
|
+
Error handler hooks should be defined before you make calls to **#every**.
|
79
|
+
|
80
|
+
You can have only one error handler per scheduler.
|
81
|
+
|
82
|
+
### All together now
|
83
|
+
|
84
|
+
Here's an example using all mentioned above:
|
85
|
+
|
86
|
+
```ruby
|
87
|
+
require 'periodic_job_mongoid'
|
88
|
+
|
89
|
+
@start = Time.now
|
90
|
+
|
91
|
+
def timestamp(string)
|
92
|
+
printf "%.3f: %s\n", Time.now - @start, string
|
93
|
+
end
|
94
|
+
|
95
|
+
scheduler = PeriodicJob::Scheduler.new
|
96
|
+
|
97
|
+
scheduler.before do |job_id|
|
98
|
+
timestamp "Before #{job_id}"
|
99
|
+
end
|
100
|
+
|
101
|
+
scheduler.after do |job_id|
|
102
|
+
timestamp "After #{job_id}"
|
103
|
+
end
|
104
|
+
|
105
|
+
scheduler.error_handler do |e, job_id|
|
106
|
+
timestamp "Error in #{job_id}: #{e.message}"
|
107
|
+
end
|
108
|
+
|
109
|
+
counter = Hash.new 0
|
110
|
+
|
111
|
+
scheduler.every 10.seconds, :foo do
|
112
|
+
counter[:foo] += 1
|
113
|
+
timestamp "Start foo, run #: #{counter[:foo]}"
|
114
|
+
raise "Foo failed" if counter[:foo] % 5 == 0
|
115
|
+
sleep 2
|
116
|
+
timestamp "End foo, run #: #{counter[:foo]}"
|
117
|
+
end
|
118
|
+
|
119
|
+
scheduler.every 15.seconds, :bar do
|
120
|
+
counter[:bar] += 1
|
121
|
+
timestamp "Start bar, run #: #{counter[:bar]}"
|
122
|
+
sleep 4
|
123
|
+
timestamp "End bar, run #: #{counter[:bar]}"
|
124
|
+
end
|
125
|
+
|
126
|
+
timestamp "Start"
|
127
|
+
|
128
|
+
loop do
|
129
|
+
sleep 1.second
|
130
|
+
scheduler.tick
|
131
|
+
end
|
132
|
+
```
|
133
|
+
|
134
|
+
Here's the output produced:
|
135
|
+
|
136
|
+
```text
|
137
|
+
0.225: Start
|
138
|
+
10.603: Before foo
|
139
|
+
10.603: Start foo, run #: 1
|
140
|
+
12.603: End foo, run #: 1
|
141
|
+
12.603: After foo
|
142
|
+
16.189: Before bar
|
143
|
+
16.189: Start bar, run #: 1
|
144
|
+
20.190: End bar, run #: 1
|
145
|
+
20.190: After bar
|
146
|
+
23.365: Before foo
|
147
|
+
23.365: Start foo, run #: 2
|
148
|
+
25.365: End foo, run #: 2
|
149
|
+
25.365: After foo
|
150
|
+
35.810: Before foo
|
151
|
+
35.810: Start foo, run #: 3
|
152
|
+
37.811: End foo, run #: 3
|
153
|
+
37.811: After foo
|
154
|
+
37.937: Before bar
|
155
|
+
37.937: Start bar, run #: 2
|
156
|
+
41.937: End bar, run #: 2
|
157
|
+
41.937: After bar
|
158
|
+
48.000: Before foo
|
159
|
+
48.000: Start foo, run #: 4
|
160
|
+
50.001: End foo, run #: 4
|
161
|
+
50.001: After foo
|
162
|
+
57.088: Before bar
|
163
|
+
57.088: Start bar, run #: 3
|
164
|
+
61.088: End bar, run #: 3
|
165
|
+
61.088: After bar
|
166
|
+
62.252: Before foo
|
167
|
+
62.252: Start foo, run #: 5
|
168
|
+
62.252: Error in foo: Foo failed
|
169
|
+
62.252: After foo
|
170
|
+
72.433: Before foo
|
171
|
+
72.433: Start foo, run #: 6
|
172
|
+
74.433: End foo, run #: 6
|
173
|
+
74.433: After foo
|
174
|
+
76.474: Before bar
|
175
|
+
76.474: Start bar, run #: 4
|
176
|
+
80.475: End bar, run #: 4
|
177
|
+
80.475: After bar
|
178
|
+
^C
|
179
|
+
```
|
180
|
+
|
181
|
+
### A more realistic example
|
182
|
+
|
183
|
+
Below is a more realistic example of using PeriodicJob, this time in a background process of a Ruby on Rails application managed by systemd on linux.
|
184
|
+
|
185
|
+
```ruby
|
186
|
+
#!/usr/bin/env -S rails runner
|
187
|
+
|
188
|
+
# it will run until it receives signal TERM
|
189
|
+
|
190
|
+
# Customize process title
|
191
|
+
Process.setproctitle 'periodic_job'
|
192
|
+
|
193
|
+
# make sure STDOUT/ERR are not buffered
|
194
|
+
$stdout.sync = true
|
195
|
+
$stderr.sync = true
|
196
|
+
|
197
|
+
require 'periodic_job_mongoid'
|
198
|
+
|
199
|
+
scheduler = PeriodicJob::Scheduler.new
|
200
|
+
|
201
|
+
scheduler.error_handler do |e, job_id|
|
202
|
+
Rails.logger.error "Periodic Job #{job_id}: #{e.message}"
|
203
|
+
Rails.logger.error e.backtrace.join "\n"
|
204
|
+
end
|
205
|
+
|
206
|
+
scheduler.before do |job_id|
|
207
|
+
# run before every job
|
208
|
+
end
|
209
|
+
|
210
|
+
scheduler.after do |job_id|
|
211
|
+
# run after every job
|
212
|
+
end
|
213
|
+
|
214
|
+
scheduler.every 30.seconds, :job_a do |job_id|
|
215
|
+
# do work here
|
216
|
+
end
|
217
|
+
|
218
|
+
# ...
|
219
|
+
|
220
|
+
$running = true
|
221
|
+
Signal.trap "TERM" do
|
222
|
+
$running = false
|
223
|
+
end
|
224
|
+
|
225
|
+
while $running do
|
226
|
+
sleep 1.second
|
227
|
+
scheduler.tick
|
228
|
+
end
|
229
|
+
```
|
230
|
+
|
231
|
+
A systemd unit file that you may use to run the process for a rails application (assuming the above script is in **script/periodic_jobs**)
|
232
|
+
|
233
|
+
```
|
234
|
+
[Unit]
|
235
|
+
Description = Periodic Job process
|
236
|
+
After = syslog.target network.target
|
237
|
+
|
238
|
+
[Service]
|
239
|
+
Type = simple
|
240
|
+
User = <user under which your rails app runs>
|
241
|
+
WorkingDirectory = <path to your rails app>
|
242
|
+
Environment = RAILS_ENV=production
|
243
|
+
|
244
|
+
ExecStart = /bin/bash -lc 'bundle exec --keep-file-descriptors rails runner script/periodoc_jobs'
|
245
|
+
|
246
|
+
KillSignal = SIGTERM
|
247
|
+
|
248
|
+
Restart = always
|
249
|
+
RestartSec = 10
|
250
|
+
|
251
|
+
StandardOutput = syslog
|
252
|
+
StandardError = syslog
|
253
|
+
SyslogIdentifier = priodic_job
|
254
|
+
SyslogFacility = local7
|
255
|
+
|
256
|
+
[Install]
|
257
|
+
WantedBy = multi-user.target
|
258
|
+
```
|
259
|
+
|
260
|
+
## Development
|
261
|
+
|
262
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
263
|
+
|
264
|
+
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).
|
265
|
+
|
266
|
+
## Contributing
|
267
|
+
|
268
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/periodic_job_mongoid.
|
269
|
+
|
270
|
+
## License
|
271
|
+
|
272
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PeriodicJob
|
4
|
+
class Checkpoint
|
5
|
+
include Mongoid::Document
|
6
|
+
include Mongoid::Timestamps::Short
|
7
|
+
|
8
|
+
# checkpoints reached this far in the future will be considered erroneous and will be treated as unreached
|
9
|
+
FUTURE_CUTOFF_TIME = 10.minutes
|
10
|
+
|
11
|
+
field :name, type: StringifiedSymbol
|
12
|
+
validates_presence_of :name
|
13
|
+
|
14
|
+
field :reached_at, type: Time
|
15
|
+
|
16
|
+
def age
|
17
|
+
Time.now - reached_at if reached_at
|
18
|
+
end
|
19
|
+
|
20
|
+
def advance_if_older_than(max_age)
|
21
|
+
time_now = Time.now.utc
|
22
|
+
result = self.class.where(id: self.id)
|
23
|
+
.and(self.class
|
24
|
+
.or(
|
25
|
+
{ reached_at: nil },
|
26
|
+
{ :reached_at.lte => time_now - max_age },
|
27
|
+
{ :reached_at.gte => time_now + FUTURE_CUTOFF_TIME },
|
28
|
+
).selector
|
29
|
+
).find_one_and_update({'$set' => { reached_at: time_now }})
|
30
|
+
|
31
|
+
!result.nil?
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.[](name)
|
35
|
+
return nil if name.to_s.strip.empty?
|
36
|
+
Checkpoint.find_or_create_by name: name
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PeriodicJob
|
4
|
+
class Job
|
5
|
+
attr_reader :interval
|
6
|
+
attr_reader :job_id
|
7
|
+
attr_reader :block
|
8
|
+
attr_reader :run_after
|
9
|
+
attr_reader :error_handler
|
10
|
+
attr_reader :before
|
11
|
+
attr_reader :after
|
12
|
+
|
13
|
+
def initialize(interval, job_id, block, error_handler, before, after)
|
14
|
+
@interval = interval
|
15
|
+
@job_id = job_id
|
16
|
+
@block = block
|
17
|
+
@error_handler = error_handler
|
18
|
+
@before = before
|
19
|
+
@after = after
|
20
|
+
schedule
|
21
|
+
end
|
22
|
+
|
23
|
+
def checkpoint
|
24
|
+
Checkpoint[@job_id]
|
25
|
+
end
|
26
|
+
|
27
|
+
def tick
|
28
|
+
if checkpoint.advance_if_older_than @run_after
|
29
|
+
run
|
30
|
+
schedule
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def run
|
37
|
+
protected_before
|
38
|
+
protected_call
|
39
|
+
protected_after
|
40
|
+
end
|
41
|
+
|
42
|
+
def protected_before
|
43
|
+
begin
|
44
|
+
@before&.call @job_id
|
45
|
+
rescue => e
|
46
|
+
error_handler&.call e, @job_id
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def protected_call
|
51
|
+
begin
|
52
|
+
@block.call @job_id
|
53
|
+
rescue => e
|
54
|
+
@error_handler&.call e, @job_id
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def protected_after
|
59
|
+
begin
|
60
|
+
@after&.call @job_id
|
61
|
+
rescue => e
|
62
|
+
@error_handler&.call e, @job_id
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def schedule
|
67
|
+
checkpoint.advance_if_older_than 0 if checkpoint.age.nil?
|
68
|
+
@run_after = checkpoint.age + @interval
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PeriodicJob
|
4
|
+
class Scheduler
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@jobs = []
|
8
|
+
end
|
9
|
+
|
10
|
+
def every(interval, job_id, &block)
|
11
|
+
@jobs << Job.new(interval, job_id, block, @error_handler, @before, @after)
|
12
|
+
end
|
13
|
+
|
14
|
+
def tick
|
15
|
+
@jobs.each &:tick
|
16
|
+
end
|
17
|
+
|
18
|
+
def error_handler(&block)
|
19
|
+
@error_handler = block
|
20
|
+
end
|
21
|
+
|
22
|
+
def before(&block)
|
23
|
+
@before = block
|
24
|
+
end
|
25
|
+
|
26
|
+
def after(&block)
|
27
|
+
@after = block
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
data/mongoid.yml
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "lib/periodic_job_mongoid/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "periodic_job_mongoid"
|
7
|
+
spec.version = PeriodicJob::VERSION
|
8
|
+
spec.authors = ["Yurie Nagorny"]
|
9
|
+
spec.email = ["ynagorny@bearincorp.com"]
|
10
|
+
|
11
|
+
spec.summary = "Coordinate and run periodic background jobs for a distributed application built using Mongoid ODM for MongoDB."
|
12
|
+
spec.description = <<EOS
|
13
|
+
Periodic Job helps you run jobs with a time interval between runs. You may have several copies of your Periodic Job process running (e.g. as part of an application server cluster) and they will coordinate their work with help of a collection stored in a shared MongoDB database to avoid duplicate runs of a job.
|
14
|
+
EOS
|
15
|
+
spec.homepage = "https://github.com/ynagorny/periodic_job_mongoid"
|
16
|
+
spec.license = "MIT"
|
17
|
+
spec.required_ruby_version = ">= 2.7.6"
|
18
|
+
|
19
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
20
|
+
spec.metadata["source_code_uri"] = "https://github.com/ynagorny/periodic_job_mongoid"
|
21
|
+
spec.metadata["changelog_uri"] = "https://github.com/ynagorny/periodic_job_mongoid/CHANGELOG.md"
|
22
|
+
|
23
|
+
# Specify which files should be added to the gem when it is released.
|
24
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
25
|
+
spec.files = Dir.chdir(__dir__) do
|
26
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
27
|
+
(f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
|
28
|
+
end
|
29
|
+
end
|
30
|
+
spec.bindir = "exe"
|
31
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
32
|
+
spec.require_paths = ["lib"]
|
33
|
+
|
34
|
+
spec.add_dependency "mongoid", "~> 7.4"
|
35
|
+
end
|
metadata
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: periodic_job_mongoid
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Yurie Nagorny
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2022-12-03 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: mongoid
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '7.4'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '7.4'
|
27
|
+
description: 'Periodic Job helps you run jobs with a time interval between runs. You
|
28
|
+
may have several copies of your Periodic Job process running (e.g. as part of an
|
29
|
+
application server cluster) and they will coordinate their work with help of a collection
|
30
|
+
stored in a shared MongoDB database to avoid duplicate runs of a job.
|
31
|
+
|
32
|
+
'
|
33
|
+
email:
|
34
|
+
- ynagorny@bearincorp.com
|
35
|
+
executables: []
|
36
|
+
extensions: []
|
37
|
+
extra_rdoc_files: []
|
38
|
+
files:
|
39
|
+
- ".rspec"
|
40
|
+
- ".ruby-version"
|
41
|
+
- CHANGELOG.md
|
42
|
+
- Gemfile
|
43
|
+
- Gemfile.lock
|
44
|
+
- LICENSE.md
|
45
|
+
- README.md
|
46
|
+
- Rakefile
|
47
|
+
- lib/periodic_job_mongoid.rb
|
48
|
+
- lib/periodic_job_mongoid/checkpoint.rb
|
49
|
+
- lib/periodic_job_mongoid/job.rb
|
50
|
+
- lib/periodic_job_mongoid/scheduler.rb
|
51
|
+
- lib/periodic_job_mongoid/version.rb
|
52
|
+
- mongoid.yml
|
53
|
+
- periodic_job_mongoid.gemspec
|
54
|
+
homepage: https://github.com/ynagorny/periodic_job_mongoid
|
55
|
+
licenses:
|
56
|
+
- MIT
|
57
|
+
metadata:
|
58
|
+
homepage_uri: https://github.com/ynagorny/periodic_job_mongoid
|
59
|
+
source_code_uri: https://github.com/ynagorny/periodic_job_mongoid
|
60
|
+
changelog_uri: https://github.com/ynagorny/periodic_job_mongoid/CHANGELOG.md
|
61
|
+
post_install_message:
|
62
|
+
rdoc_options: []
|
63
|
+
require_paths:
|
64
|
+
- lib
|
65
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 2.7.6
|
70
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '0'
|
75
|
+
requirements: []
|
76
|
+
rubygems_version: 3.3.7
|
77
|
+
signing_key:
|
78
|
+
specification_version: 4
|
79
|
+
summary: Coordinate and run periodic background jobs for a distributed application
|
80
|
+
built using Mongoid ODM for MongoDB.
|
81
|
+
test_files: []
|