schked 0.4.0 → 1.1.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: 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