schked 0.3.3 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d63d5c90dcb6ba4e576ebd9ac7f07d8f8169473836bd38d656a69d550e08c304
4
- data.tar.gz: b33124e9bd3125d1cf93d418c165e275d6ab89d727938313fdb7b492cd35861e
3
+ metadata.gz: 49298780af4e1af7294623211a41bf0e0bbe2fa3238ac28d24074a8ce7102b36
4
+ data.tar.gz: 5320ec84af3ba28e82627f28c68ef3278542ab1e37191b48d7feaf65cdaef354
5
5
  SHA512:
6
- metadata.gz: 83dd4020439f63a22f1cf3226b538a408adb5b444df1bb6e9def70d07f56d18c6084dfc58d5d3c7f226e3edeb61192d825ff556a5f56c8996195331bfee82670
7
- data.tar.gz: 002453deb8896a3c248148cd904fac48910118c3cee0b1e2485ad0b98db39ea36550d623501e4eaa51d52c3f3150ada08915fe385ea99509f34bc7f4a7d98928
6
+ metadata.gz: 4efb829f41f8c13b94bf509b1b89688294142c61ada430d929a4a48322ee1be0d094a6492709348796b36aaf0479f3038c4db77402f169dcfa1e4343d5f31b30
7
+ data.tar.gz: a3d24f54e91d2dc3c470749ebbd37e21d3a804befd00f46c43e32f6a85885f70c1cdd1bf052fa928497c619da1921e700c20b312f2088e2eeffdd618adbac370
data/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
 
4
4
  # Schked
5
5
 
6
- Framework agnostic Rufus-scheduler wrapper to run recurring jobs.
6
+ Framework agnostic [Rufus-scheduler](https://github.com/jmettraux/rufus-scheduler) wrapper to run recurring jobs.
7
7
 
8
8
  <a href="https://evilmartians.com/?utm_source=schked">
9
9
  <img src="https://evilmartians.com/badges/sponsored-by-evil-martians.svg" alt="Sponsored by Evil Martians" width="236" height="54"></a>
@@ -41,7 +41,7 @@ gem install schked
41
41
  config/schedule.rb
42
42
 
43
43
  ```ruby
44
- cron "*/30 * * * *", as: "CleanOrphanAttachmentsJob" do
44
+ cron "*/30 * * * *", as: "CleanOrphanAttachmentsJob", timeout: "60s", overlap: false do
45
45
  CleanOrphanAttachmentsJob.perform_later
46
46
  end
47
47
  ```
@@ -72,6 +72,16 @@ To show schedule:
72
72
  bundle exec schked show
73
73
  ```
74
74
 
75
+ ### Duplicate scheduling
76
+
77
+ When you deploy your schedule to production, you want to start new instance before you shut down the current. And you don't want simultaneous working of both. To achieve a seamless transition, Schked is using Redis for locks.
78
+
79
+ You can configure Redis client as the following:
80
+
81
+ ```ruby
82
+ Schked.config.redis_servers = ["redis://127.0.0.1:7777", "redis://127.0.0.1:7778", "redis://127.0.0.1:7779"]
83
+ ```
84
+
75
85
  ### Callbacks
76
86
 
77
87
  Also, you can define callbacks for errors handling:
data/lib/schked/cli.rb CHANGED
@@ -45,9 +45,7 @@ module Schked
45
45
  private
46
46
 
47
47
  def load_requires
48
- if options[:require]&.any?
49
- options[:require].each { |file| load(File.join(Dir.pwd, file)) }
50
- end
48
+ options[:require].each { |file| load(File.join(Dir.pwd, file)) } if options[:require]&.any?
51
49
 
52
50
  # We have to load Schked at here, because of Rails and our railtie.
53
51
  require "schked"
data/lib/schked/config.rb CHANGED
@@ -4,7 +4,9 @@ require "logger"
4
4
 
5
5
  module Schked
6
6
  class Config
7
- attr_writer :logger
7
+ attr_writer :logger,
8
+ :do_not_load_root_schedule,
9
+ :redis_servers
8
10
 
9
11
  def paths
10
12
  @paths ||= []
@@ -18,6 +20,10 @@ module Schked
18
20
  @logger ||= Logger.new($stdout).tap { |l| l.level = Logger::INFO }
19
21
  end
20
22
 
23
+ def do_not_load_root_schedule?
24
+ !!@do_not_load_root_schedule
25
+ end
26
+
21
27
  def register_callback(name, &block)
22
28
  callbacks[name] << block
23
29
  end
@@ -28,6 +34,10 @@ module Schked
28
34
  end
29
35
  end
30
36
 
37
+ def redis_servers
38
+ @redis_servers ||= [ENV["REDIS_URL"]]
39
+ end
40
+
31
41
  private
32
42
 
33
43
  def callbacks
@@ -6,7 +6,10 @@ module Schked
6
6
  class Railtie < Rails::Railtie
7
7
  class PathsConfig
8
8
  def self.call(app)
9
- if (root_schedule = app.root.join("config", "schedule.rb")).exist?
9
+ return if Schked.config.do_not_load_root_schedule?
10
+
11
+ root_schedule = app.root.join("config", "schedule.rb")
12
+ if root_schedule.exist?
10
13
  path = root_schedule.to_s
11
14
  Schked.config.paths << path unless Schked.config.paths.include?(path)
12
15
  end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Schked
4
+ class RedisLocker
5
+ attr_reader :lock_manager,
6
+ :lock_id,
7
+ :lock_ttl
8
+
9
+ LOCK_KEY = "schked:redis_locker"
10
+ LOCK_TTL = 60_000 # ms
11
+
12
+ def initialize(redis_servers, lock_ttl: LOCK_TTL)
13
+ @lock_manager = Redlock::Client.new(redis_servers, retry_count: 0)
14
+ @lock_ttl = lock_ttl
15
+ end
16
+
17
+ def lock
18
+ valid_lock? || !!try_lock
19
+ end
20
+
21
+ def unlock
22
+ lock_manager.unlock(lock_id) if valid_lock?
23
+ end
24
+
25
+ def extend_lock
26
+ return false unless valid_lock?
27
+
28
+ @lock_id = lock_manager.lock(LOCK_KEY, lock_ttl, extend: lock_id, extend_only_if_locked: true)
29
+
30
+ !!@lock_id
31
+ end
32
+
33
+ def valid_lock?
34
+ return false unless lock_id
35
+
36
+ lock_manager.valid_lock?(lock_id)
37
+ end
38
+
39
+ private
40
+
41
+ def try_lock
42
+ @lock_id = lock_manager.lock(LOCK_KEY, lock_ttl)
43
+ end
44
+ end
45
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Schked
4
- VERSION = "0.3.3"
4
+ VERSION = "1.0.0"
5
5
  end
data/lib/schked/worker.rb CHANGED
@@ -7,9 +7,12 @@ module Schked
7
7
  def initialize(config:)
8
8
  @config = config
9
9
 
10
- @scheduler = Rufus::Scheduler.new
10
+ @locker = RedisLocker.new(config.redis_servers, lock_ttl: 40_000)
11
+ @scheduler = Rufus::Scheduler.new(trigger_lock: locker)
11
12
 
13
+ watch_signals
12
14
  define_callbacks
15
+ define_extend_lock
13
16
  load_schedule
14
17
  end
15
18
 
@@ -38,7 +41,7 @@ module Schked
38
41
 
39
42
  private
40
43
 
41
- attr_reader :config, :scheduler
44
+ attr_reader :config, :scheduler, :locker
42
45
 
43
46
  def define_callbacks
44
47
  cfg = config
@@ -63,6 +66,31 @@ module Schked
63
66
  end
64
67
  end
65
68
 
69
+ def watch_signals
70
+ Signal.trap("TERM") do
71
+ config.logger.info("Going to shut down...")
72
+ @shutdown = true
73
+ end
74
+
75
+ Signal.trap("INT") do
76
+ config.logger.info("Going to shut down...")
77
+ @shutdown = true
78
+ end
79
+
80
+ Thread.new do
81
+ loop do
82
+ scheduler.shutdown(wait: 5) if @shutdown
83
+ sleep 0.2
84
+ end
85
+ end
86
+ end
87
+
88
+ def define_extend_lock
89
+ scheduler.every("10s", as: "Schked::Worker#extend_lock", timeout: "5s", overlap: false) do
90
+ locker.extend_lock
91
+ end
92
+ end
93
+
66
94
  def load_schedule
67
95
  scheduler.instance_eval(schedule)
68
96
  end
data/lib/schked.rb CHANGED
@@ -1,8 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "redlock"
4
+
3
5
  require "schked/version"
4
6
  require "schked/config"
5
7
  require "schked/worker"
8
+ require "schked/redis_locker"
6
9
  require "schked/railtie" if defined?(Rails)
7
10
 
8
11
  module Schked
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: schked
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.3
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Misha Merkushin
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-07-09 00:00:00.000000000 Z
11
+ date: 2022-10-10 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: redlock
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: rufus-scheduler
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -151,6 +165,7 @@ files:
151
165
  - lib/schked/cli.rb
152
166
  - lib/schked/config.rb
153
167
  - lib/schked/railtie.rb
168
+ - lib/schked/redis_locker.rb
154
169
  - lib/schked/version.rb
155
170
  - lib/schked/worker.rb
156
171
  homepage: https://github.com/bibendi/schked
@@ -173,7 +188,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
173
188
  - !ruby/object:Gem::Version
174
189
  version: '0'
175
190
  requirements: []
176
- rubygems_version: 3.1.2
191
+ rubygems_version: 3.2.32
177
192
  signing_key:
178
193
  specification_version: 4
179
194
  summary: Ruby Scheduler