schked 0.4.0 → 1.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d8543c91732ed3147359b2b6c5c8ebef8ddb2f467766a8c143072ac403341647
4
- data.tar.gz: d85da0ba7c7f001d9682c9423a59bb35a8ff4c3581583fbf1ed31cc11650c866
3
+ metadata.gz: 660370465a4ff279d9bc26184108c07637d0fcd69f071e22e8dea9b5b79b29ae
4
+ data.tar.gz: 9e54efb1455b8e48af9d67011008cb821aefe6bb691ec0325f1f0a9c8425ed10
5
5
  SHA512:
6
- metadata.gz: 4e87e77de1530b23d680660806edafba03eb191c72da03d8b37b2b0a3845916ce700924e9d51806a20fbba58d8c1c9403e2a9c8d7c33103ab0eb4ceeaa76cdd1
7
- data.tar.gz: 4408c7dfb6b03b73018d2d80cd005e52b6ddfc28635fd6eca972c670f17add271e2e89501452220dec116d48c529cb4d5f43761cc7416ddd6a247a3444ee713d
6
+ metadata.gz: f0ee5ac189460a35c87da4c6a889482f824985abd5561f5541e01157b6574e7d28957b9082cab272fc2cce04e779850ced784858e91da02554b3816a37483fee
7
+ data.tar.gz: 68754d88a2fcc870c6e161d951b2ff2a00749d0c345401e9608c69cfde4055554f1275157b1e7af7c3a2f07efff037eb690619834daa3d289293ef33c27be6ac
data/README.md CHANGED
@@ -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
@@ -5,7 +5,9 @@ require "logger"
5
5
  module Schked
6
6
  class Config
7
7
  attr_writer :logger,
8
- :do_not_load_root_schedule
8
+ :do_not_load_root_schedule,
9
+ :redis_servers,
10
+ :standalone
9
11
 
10
12
  def paths
11
13
  @paths ||= []
@@ -33,6 +35,16 @@ module Schked
33
35
  end
34
36
  end
35
37
 
38
+ def redis_servers
39
+ @redis_servers ||= [ENV.fetch("REDIS_URL", "redis://127.0.0.1:6379")]
40
+ end
41
+
42
+ def standalone?
43
+ @standalone = ENV["RAILS_ENV"] == "test" || ENV["RACK_ENV"] == "test" if @standalone.nil?
44
+
45
+ !!@standalone
46
+ end
47
+
36
48
  private
37
49
 
38
50
  def callbacks
@@ -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.4.0"
4
+ VERSION = "1.1.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) unless config.standalone?
11
+ @scheduler = Rufus::Scheduler.new(trigger_lock: locker)
11
12
 
13
+ watch_signals
12
14
  define_callbacks
15
+ define_extend_lock unless config.standalone?
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.4.0
4
+ version: 1.1.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: 2022-08-12 00:00:00.000000000 Z
11
+ date: 2022-10-19 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