kaal 0.5.0 → 0.6.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.
Files changed (101) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +24 -24
  3. data/config/kaal.yml +12 -0
  4. data/lib/kaal/backend/adapter.rb +4 -0
  5. data/lib/kaal/cli.rb +38 -34
  6. data/lib/kaal/config/backend_factory.rb +178 -0
  7. data/lib/kaal/config/configuration.rb +65 -7
  8. data/lib/kaal/config/file_loader.rb +187 -0
  9. data/lib/kaal/config.rb +2 -0
  10. data/lib/kaal/runtime/scheduler_boot_loader.rb +1 -1
  11. data/lib/kaal/scheduler_file/loader.rb +1 -1
  12. data/lib/kaal/version.rb +1 -1
  13. data/lib/kaal.rb +7 -0
  14. data/sig/00_types.rbs +12 -0
  15. data/sig/dependencies.rbs +49 -0
  16. data/sig/kaal/active_record_support.rbs +23 -0
  17. data/sig/kaal/backend/adapter.rbs +26 -0
  18. data/sig/kaal/backend/dispatch_attempt_logger.rbs +17 -0
  19. data/sig/kaal/backend/dispatch_logging.rbs +23 -0
  20. data/sig/kaal/backend/dispatch_registry_accessor.rbs +17 -0
  21. data/sig/kaal/backend/memory_adapter.rbs +33 -0
  22. data/sig/kaal/backend/mysql.rbs +25 -0
  23. data/sig/kaal/backend/postgres.rbs +19 -0
  24. data/sig/kaal/backend/redis_adapter.rbs +41 -0
  25. data/sig/kaal/backend/sqlite.rbs +19 -0
  26. data/sig/kaal/cli.rbs +41 -0
  27. data/sig/kaal/config/backend_factory.rbs +41 -0
  28. data/sig/kaal/config/configuration.rbs +70 -0
  29. data/sig/kaal/config/delayed_job_security_policy.rbs +19 -0
  30. data/sig/kaal/config/file_loader.rbs +35 -0
  31. data/sig/kaal/config/scheduler_config_error.rbs +4 -0
  32. data/sig/kaal/config/scheduler_time_zone_resolver.rbs +19 -0
  33. data/sig/kaal/config.rbs +11 -0
  34. data/sig/kaal/core/coordinator.rbs +103 -0
  35. data/sig/kaal/core/enabled_entry_enumerator.rbs +21 -0
  36. data/sig/kaal/core/occurrence_finder.rbs +9 -0
  37. data/sig/kaal/core.rbs +9 -0
  38. data/sig/kaal/definition/database_engine.rbs +25 -0
  39. data/sig/kaal/definition/memory_engine.rbs +23 -0
  40. data/sig/kaal/definition/persistence_helpers.rbs +9 -0
  41. data/sig/kaal/definition/redis_engine.rbs +33 -0
  42. data/sig/kaal/definition/registry.rbs +29 -0
  43. data/sig/kaal/definitions/registration_service.rbs +27 -0
  44. data/sig/kaal/definitions/registry_accessor.rbs +17 -0
  45. data/sig/kaal/delayed_job/database_engine.rbs +37 -0
  46. data/sig/kaal/delayed_job/dispatch_failure_logger.rbs +7 -0
  47. data/sig/kaal/delayed_job/memory_engine.rbs +29 -0
  48. data/sig/kaal/delayed_job/mysql_version_support.rbs +15 -0
  49. data/sig/kaal/delayed_job/redis_engine.rbs +31 -0
  50. data/sig/kaal/delayed_job/registry.rbs +20 -0
  51. data/sig/kaal/dispatch/database_engine.rbs +39 -0
  52. data/sig/kaal/dispatch/memory_engine.rbs +23 -0
  53. data/sig/kaal/dispatch/redis_engine.rbs +25 -0
  54. data/sig/kaal/dispatch/registry.rbs +11 -0
  55. data/sig/kaal/internal/active_record/base_record.rbs +8 -0
  56. data/sig/kaal/internal/active_record/connection_support.rbs +25 -0
  57. data/sig/kaal/internal/active_record/database_backend.rbs +37 -0
  58. data/sig/kaal/internal/active_record/definition_record.rbs +8 -0
  59. data/sig/kaal/internal/active_record/definition_registry.rbs +27 -0
  60. data/sig/kaal/internal/active_record/delayed_job_record.rbs +8 -0
  61. data/sig/kaal/internal/active_record/delayed_job_registry.rbs +39 -0
  62. data/sig/kaal/internal/active_record/dispatch_record.rbs +8 -0
  63. data/sig/kaal/internal/active_record/dispatch_registry.rbs +43 -0
  64. data/sig/kaal/internal/active_record/lock_record.rbs +8 -0
  65. data/sig/kaal/internal/active_record/migration_templates.rbs +17 -0
  66. data/sig/kaal/internal/active_record/mysql_backend.rbs +45 -0
  67. data/sig/kaal/internal/active_record/postgres_backend.rbs +41 -0
  68. data/sig/kaal/internal/active_record.rbs +0 -0
  69. data/sig/kaal/internal/sequel/database_backend.rbs +39 -0
  70. data/sig/kaal/internal/sequel/mysql_backend.rbs +47 -0
  71. data/sig/kaal/internal/sequel/postgres_backend.rbs +43 -0
  72. data/sig/kaal/internal/sequel.rbs +0 -0
  73. data/sig/kaal/job_dispatcher.rbs +19 -0
  74. data/sig/kaal/persistence/database.rbs +19 -0
  75. data/sig/kaal/persistence/migration_templates.rbs +15 -0
  76. data/sig/kaal/register_conflict_support.rbs +11 -0
  77. data/sig/kaal/registry.rbs +44 -0
  78. data/sig/kaal/runtime/runtime_context.rbs +23 -0
  79. data/sig/kaal/runtime/scheduler_boot_loader.rbs +23 -0
  80. data/sig/kaal/runtime/signal_handler_chain.rbs +19 -0
  81. data/sig/kaal/runtime/signal_handler_installer.rbs +19 -0
  82. data/sig/kaal/runtime.rbs +11 -0
  83. data/sig/kaal/scheduler_file/hash_transform.rbs +9 -0
  84. data/sig/kaal/scheduler_file/helper_bundle.rbs +15 -0
  85. data/sig/kaal/scheduler_file/job_applier.rbs +43 -0
  86. data/sig/kaal/scheduler_file/job_normalizer.rbs +27 -0
  87. data/sig/kaal/scheduler_file/loader.rbs +69 -0
  88. data/sig/kaal/scheduler_file/payload_loader.rbs +33 -0
  89. data/sig/kaal/scheduler_file/placeholder_support.rbs +19 -0
  90. data/sig/kaal/scheduler_file.rbs +9 -0
  91. data/sig/kaal/sequel_support.rbs +25 -0
  92. data/sig/kaal/support/hash_tools.rbs +27 -0
  93. data/sig/kaal/utils/cron_humanizer.rbs +39 -0
  94. data/sig/kaal/utils/cron_utils.rbs +43 -0
  95. data/sig/kaal/utils/idempotency_key_generator.rbs +5 -0
  96. data/sig/kaal/utils.rbs +9 -0
  97. data/sig/kaal/version.rbs +3 -0
  98. data/sig/kaal.rbs +145 -0
  99. metadata +90 -3
  100. data/config/kaal.rb +0 -15
  101. /data/config/{scheduler.yml → kaal-scheduler.yml} +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c304a5d511f122e3b14d0a6dfa88153d49fbf8bf2ddec699340545831c38e0af
4
- data.tar.gz: c03c7fa46c7f301d100ed38bca9e9d96e3451bd69d18b801b654f8fb97bee9a8
3
+ metadata.gz: 0d8c12714ca483db0abe733287ae543ecd206e58c02b228983aa0fb506671542
4
+ data.tar.gz: 3236ad6f7721d80424a3b725542c66f0633a3a21ed42d52514a5294b75013461
5
5
  SHA512:
6
- metadata.gz: b65e18daf9353b7bdd1bf4709aa8175eb1be0e7e76b68b55f2163be96e9db800adc87c075512a7c0b0a44e34f85b56055f2f9f4db165e3187ca1067a17195775
7
- data.tar.gz: 6b148d15f5f3092af565d47b8120c7bf52b28ba198fbbc14a5fcb2aad516aaf6de6d4814e5f583e494c00b1b7bdb283d7f2a8eb7dc8879eaf6bd1005c9584383
6
+ metadata.gz: a7213b5670154c87e4115761d62ee9e5955bd84e520ac3d3dc44dc5118c7840aeb6b6d82ad7f71aa8fe1b7fb32430abd59f636c2d14a0450054af6de7c695105
7
+ data.tar.gz: 790385b5f77017e8001e2c9c3e38484acdab8e733dc5cd15d1bf0a39c6c2074fa9756c7226328446bc69b9044895b0644bdb521725b6992780265d5d0a296fb0
data/README.md CHANGED
@@ -21,43 +21,43 @@ bundle exec kaal init --backend=memory
21
21
 
22
22
  `kaal init` creates:
23
23
 
24
- - `config/kaal.rb`
25
- - `config/scheduler.yml`
24
+ - `config/kaal.yml`
25
+ - `config/kaal-scheduler.yml`
26
26
 
27
27
  Supported backends:
28
28
 
29
29
  - `memory`
30
30
  - `redis`
31
31
 
32
- If you want SQL persistence instead, add the runtime libraries your app uses, such as `sequel`, `activerecord`, `sqlite3`, `pg`, or `mysql2`, then configure one of the explicit `Kaal::Backend::*` SQL backends.
32
+ If you want SQL persistence instead, add the runtime libraries your app uses, such as `sequel`, `activerecord`, `sqlite3`, `pg`, or `mysql2`, then set `backend: sqlite/postgres/mysql` plus `backend_config` in `config/kaal.yml`.
33
33
 
34
34
  ## Configuration
35
35
 
36
- Generated `config/kaal.rb` is the primary entrypoint:
36
+ Generated `config/kaal.yml` is the primary entrypoint:
37
37
 
38
- ```ruby
39
- require 'kaal'
40
-
41
- Kaal.configure do |config|
42
- config.backend = Kaal::Backend::MemoryAdapter.new
43
- config.tick_interval = 5
44
- config.window_lookback = 120
45
- config.lease_ttl = 125
46
- config.scheduler_config_path = 'config/scheduler.yml'
47
- end
38
+ ```yaml
39
+ defaults:
40
+ backend: memory
41
+ namespace: kaal
42
+ tick_interval: 5
43
+ window_lookback: 120
44
+ window_lookahead: 0
45
+ lease_ttl: 125
46
+ scheduler_config_path: config/kaal-scheduler.yml
47
+ enable_dispatch_recovery: true
48
+ enable_log_dispatch_registry: false
49
+ delayed_job_allowed_class_prefixes: []
50
+ backend_config: {}
48
51
  ```
49
52
 
50
53
  Redis path:
51
54
 
52
- ```ruby
53
- require 'redis'
54
-
55
- redis = Redis.new(url: ENV.fetch('REDIS_URL'))
56
-
57
- Kaal.configure do |config|
58
- config.backend = Kaal::Backend::RedisAdapter.new(redis)
59
- config.scheduler_config_path = 'config/scheduler.yml'
60
- end
55
+ ```yaml
56
+ defaults:
57
+ backend: redis
58
+ scheduler_config_path: config/kaal-scheduler.yml
59
+ backend_config:
60
+ url: redis://127.0.0.1:6379/0
61
61
  ```
62
62
 
63
63
  Time zone behavior is explicit:
@@ -67,7 +67,7 @@ Time zone behavior is explicit:
67
67
 
68
68
  ## Scheduler File
69
69
 
70
- Default scheduler definitions live at `config/scheduler.yml`:
70
+ Default scheduler definitions live at `config/kaal-scheduler.yml`:
71
71
 
72
72
  ```yaml
73
73
  defaults:
data/config/kaal.yml ADDED
@@ -0,0 +1,12 @@
1
+ defaults:
2
+ backend: memory
3
+ namespace: kaal
4
+ tick_interval: 5
5
+ window_lookback: 120
6
+ window_lookahead: 0
7
+ lease_ttl: 125
8
+ scheduler_config_path: config/kaal-scheduler.yml
9
+ enable_dispatch_recovery: true
10
+ enable_log_dispatch_registry: false
11
+ delayed_job_allowed_class_prefixes: []
12
+ backend_config: {}
@@ -98,6 +98,10 @@ module Kaal
98
98
  def delayed_store
99
99
  nil
100
100
  end
101
+
102
+ def disconnect_for_fork
103
+ nil
104
+ end
101
105
  end
102
106
 
103
107
  ##
data/lib/kaal/cli.rb CHANGED
@@ -19,10 +19,10 @@ module Kaal
19
19
  def load_project!
20
20
  Kaal.reset_configuration!
21
21
  Kaal.reset_registry!
22
- load config_path
23
- Kaal.warn_on_risky_configuration!
24
22
  runtime_context = RuntimeContext.default(root_path: root_path)
25
- Kaal.load_scheduler_file!(runtime_context: runtime_context) if File.exist?(scheduler_path)
23
+ Kaal.load_config_file!(path: config_path, runtime_context:)
24
+ Kaal.warn_on_risky_configuration!
25
+ Kaal.load_scheduler_file!(runtime_context: runtime_context)
26
26
  end
27
27
 
28
28
  def root_path
@@ -33,42 +33,46 @@ module Kaal
33
33
  config = options[:config]
34
34
  return File.expand_path(config) if config
35
35
 
36
- File.join(root_path, 'config', 'kaal.rb')
36
+ File.join(root_path, 'config', 'kaal.yml')
37
37
  end
38
38
 
39
39
  def scheduler_path
40
- File.join(root_path, 'config', 'scheduler.yml')
40
+ File.join(root_path, 'config', 'kaal-scheduler.yml')
41
41
  end
42
42
 
43
43
  def render_config_template(backend)
44
44
  case backend
45
45
  when 'memory'
46
- <<~RUBY
47
- require 'kaal'
48
-
49
- Kaal.configure do |config|
50
- config.backend = Kaal::Backend::MemoryAdapter.new
51
- config.tick_interval = 5
52
- config.window_lookback = 120
53
- config.lease_ttl = 125
54
- config.scheduler_config_path = 'config/scheduler.yml'
55
- end
56
- RUBY
46
+ <<~YAML
47
+ defaults:
48
+ backend: memory
49
+ namespace: kaal
50
+ tick_interval: 5
51
+ window_lookback: 120
52
+ window_lookahead: 0
53
+ lease_ttl: 125
54
+ scheduler_config_path: config/kaal-scheduler.yml
55
+ enable_dispatch_recovery: true
56
+ enable_log_dispatch_registry: false
57
+ delayed_job_allowed_class_prefixes: []
58
+ backend_config: {}
59
+ YAML
57
60
  when 'redis'
58
- <<~RUBY
59
- require 'kaal'
60
- require 'redis'
61
-
62
- redis = Redis.new(url: ENV.fetch('REDIS_URL'))
63
-
64
- Kaal.configure do |config|
65
- config.backend = Kaal::Backend::RedisAdapter.new(redis, namespace: 'kaal')
66
- config.tick_interval = 5
67
- config.window_lookback = 120
68
- config.lease_ttl = 125
69
- config.scheduler_config_path = 'config/scheduler.yml'
70
- end
71
- RUBY
61
+ <<~YAML
62
+ defaults:
63
+ backend: redis
64
+ namespace: kaal
65
+ tick_interval: 5
66
+ window_lookback: 120
67
+ window_lookahead: 0
68
+ lease_ttl: 125
69
+ scheduler_config_path: config/kaal-scheduler.yml
70
+ enable_dispatch_recovery: true
71
+ enable_log_dispatch_registry: false
72
+ delayed_job_allowed_class_prefixes: []
73
+ backend_config:
74
+ url: redis://127.0.0.1:6379/0
75
+ YAML
72
76
  else
73
77
  raise Thor::Error, "Unsupported backend '#{backend}'"
74
78
  end
@@ -95,9 +99,9 @@ module Kaal
95
99
  package_name 'kaal'
96
100
 
97
101
  class_option :root, type: :string, default: Dir.pwd, desc: 'Project root'
98
- class_option :config, type: :string, desc: 'Path to config/kaal.rb'
102
+ class_option :config, type: :string, desc: 'Path to config/kaal.yml'
99
103
 
100
- desc 'init', 'Generate config/kaal.rb and config/scheduler.yml'
104
+ desc 'init', 'Generate config/kaal.yml and config/kaal-scheduler.yml'
101
105
  option :backend, type: :string, default: 'memory', enum: %w[memory redis]
102
106
  def init
103
107
  root = File.expand_path(options[:root])
@@ -105,8 +109,8 @@ module Kaal
105
109
  writer = self.class
106
110
  FileUtils.mkdir_p(File.join(root, 'config'))
107
111
 
108
- writer.write_file(File.join(root, 'config', 'kaal.rb'), render_config_template(backend))
109
- writer.write_file(File.join(root, 'config', 'scheduler.yml'), scheduler_template)
112
+ writer.write_file(File.join(root, 'config', 'kaal.yml'), render_config_template(backend))
113
+ writer.write_file(File.join(root, 'config', 'kaal-scheduler.yml'), scheduler_template)
110
114
 
111
115
  say("Initialized Kaal project for #{backend} backend")
112
116
  end
@@ -0,0 +1,178 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright Codevedas Inc. 2025-present
4
+ #
5
+ # This source code is licensed under the MIT license found in the
6
+ # LICENSE file in the root directory of this source tree.
7
+ require 'fileutils'
8
+ require 'pathname'
9
+ require 'uri'
10
+
11
+ module Kaal
12
+ module Config
13
+ # Builds backend adapter instances from symbolic runtime configuration.
14
+ module BackendFactory
15
+ module_function
16
+
17
+ SUPPORTED_BACKENDS = %w[memory redis sqlite postgres mysql].freeze
18
+
19
+ def normalize_name(name)
20
+ normalized = name.to_s.strip.downcase
21
+ return nil if normalized.empty?
22
+
23
+ normalized = 'postgres' if normalized == 'postgresql'
24
+ normalized = 'mysql' if normalized == 'trilogy'
25
+ return normalized if SUPPORTED_BACKENDS.include?(normalized)
26
+
27
+ raise Kaal::ConfigurationError, "Unsupported backend #{name.inspect}; use memory, redis, sqlite, postgres, or mysql"
28
+ end
29
+
30
+ def build(name, backend_config:, namespace:, runtime_context: nil)
31
+ backend_name = normalize_name(name)
32
+ config = normalize_backend_config(backend_config)
33
+
34
+ case backend_name
35
+ when 'memory'
36
+ Kaal::Backend::MemoryAdapter.new
37
+ when 'redis'
38
+ build_redis_backend(config, namespace)
39
+ when 'sqlite'
40
+ build_sqlite_backend(config, namespace, runtime_context)
41
+ when 'postgres'
42
+ build_postgres_backend(config, namespace)
43
+ when 'mysql'
44
+ build_mysql_backend(config, namespace)
45
+ end
46
+ end
47
+
48
+ def normalize_backend_config(backend_config)
49
+ hash = backend_config.is_a?(Hash) ? backend_config : {}
50
+ Kaal::Support::HashTools.symbolize_keys(Kaal::Support::HashTools.deep_dup(hash))
51
+ end
52
+
53
+ def build_redis_backend(config, namespace)
54
+ require_redis!
55
+
56
+ url = string_value(config[:url])
57
+ raise Kaal::ConfigurationError, 'redis backend requires backend_config.url or KAAL_BACKEND_URL' if url.empty?
58
+
59
+ Kaal::Backend::RedisAdapter.new(::Redis.new(url: url), namespace:)
60
+ end
61
+
62
+ def build_sqlite_backend(config, namespace, runtime_context)
63
+ return Kaal::Backend::SQLite.new(connection: build_sqlite_connection(config[:connection], runtime_context), namespace:) if config.key?(:connection)
64
+
65
+ url = string_value(config[:url])
66
+ database = string_value(config[:database])
67
+ target = url.empty? ? database : url
68
+ raise Kaal::ConfigurationError, 'sqlite backend requires backend_config.url, backend_config.database, or KAAL_BACKEND_URL' if target.empty?
69
+
70
+ require_sequel!
71
+ Kaal::Backend::SQLite.new(
72
+ database: sequel_sqlite_database(target, runtime_context),
73
+ namespace:
74
+ )
75
+ end
76
+
77
+ def build_postgres_backend(config, namespace)
78
+ if config.key?(:connection)
79
+ return Kaal::Backend::Postgres.new(connection: normalize_connection_hash(config[:connection], 'postgresql', nil),
80
+ namespace:)
81
+ end
82
+
83
+ url = string_value(config[:url])
84
+ raise Kaal::ConfigurationError, 'postgres backend requires backend_config.url or KAAL_BACKEND_URL' if url.empty?
85
+
86
+ require_sequel!
87
+ Kaal::Backend::Postgres.new(database: ::Sequel.connect(url), namespace:)
88
+ end
89
+
90
+ def build_mysql_backend(config, namespace)
91
+ use_skip_locked = config[:use_skip_locked]
92
+ skip_locked_configured = config.key?(:use_skip_locked)
93
+
94
+ if config.key?(:connection)
95
+ connection = normalize_connection_hash(config[:connection], 'mysql2', nil)
96
+ return Kaal::Backend::MySQL.new(connection:, namespace:) unless skip_locked_configured
97
+
98
+ return Kaal::Backend::MySQL.new(connection:, namespace:, use_skip_locked:)
99
+ end
100
+
101
+ url = string_value(config[:url])
102
+ raise Kaal::ConfigurationError, 'mysql backend requires backend_config.url or KAAL_BACKEND_URL' if url.empty?
103
+
104
+ require_sequel!
105
+ database = ::Sequel.connect(url)
106
+ return Kaal::Backend::MySQL.new(database:, namespace:) unless skip_locked_configured
107
+
108
+ Kaal::Backend::MySQL.new(database:, namespace:, use_skip_locked:)
109
+ end
110
+
111
+ def build_sqlite_connection(connection, runtime_context)
112
+ normalize_connection_hash(connection, 'sqlite3', runtime_context)
113
+ end
114
+
115
+ def normalize_connection_hash(connection, default_adapter, runtime_context)
116
+ case connection
117
+ when String
118
+ connection
119
+ when Hash
120
+ normalized = Kaal::Support::HashTools.symbolize_keys(Kaal::Support::HashTools.deep_dup(connection))
121
+ adapter = string_value(normalized[:adapter])
122
+ normalized_adapter = adapter.empty? ? default_adapter : adapter
123
+ normalized[:adapter] = normalized_adapter
124
+ normalized[:database] = resolve_sqlite_database_path(normalized[:database], runtime_context) if normalized_adapter == 'sqlite3'
125
+ normalized
126
+ else
127
+ raise Kaal::ConfigurationError, 'backend_config.connection must be a URL string or hash'
128
+ end
129
+ end
130
+
131
+ def resolve_sqlite_database_path(database, runtime_context)
132
+ value = string_value(database)
133
+ return value if value.empty?
134
+ return value if sqlite_uri?(value)
135
+ return ensure_sqlite_directory!(value) if Pathname.new(value).absolute?
136
+
137
+ resolved = runtime_context ? runtime_context.resolve_path(value) : value
138
+ ensure_sqlite_directory!(resolved)
139
+ end
140
+
141
+ def sqlite_uri?(value)
142
+ value.start_with?('sqlite:', 'file:')
143
+ end
144
+
145
+ def sequel_sqlite_database(target, runtime_context)
146
+ return ::Sequel.connect(target) if sqlite_uri?(target)
147
+
148
+ ::Sequel.connect(adapter: 'sqlite', database: resolve_sqlite_database_path(target, runtime_context))
149
+ end
150
+
151
+ def ensure_sqlite_directory!(database_path)
152
+ directory = File.dirname(database_path)
153
+ FileUtils.mkdir_p(directory) unless directory == '.' || directory.empty?
154
+ database_path
155
+ end
156
+
157
+ def adapter_name_for_error(adapter)
158
+ adapter == 'postgresql' ? 'postgres' : 'mysql'
159
+ end
160
+
161
+ def string_value(value)
162
+ value.to_s.strip
163
+ end
164
+
165
+ def require_redis!
166
+ require 'redis'
167
+ rescue LoadError => e
168
+ raise LoadError, "#{e.message}. Add `gem 'redis'` to your Gemfile to use the Redis-backed Kaal adapter.", cause: e
169
+ end
170
+
171
+ def require_sequel!
172
+ require 'sequel'
173
+ rescue LoadError => e
174
+ raise LoadError, "#{e.message}. Add `gem 'sequel'` to your Gemfile to use Sequel-backed Kaal SQL support.", cause: e
175
+ end
176
+ end
177
+ end
178
+ end
@@ -12,7 +12,8 @@ module Kaal
12
12
  # @example Basic configuration
13
13
  # Kaal.configure do |config|
14
14
  # config.tick_interval = 5
15
- # config.backend = Kaal::Backend::RedisAdapter.new(Redis.new(url: ENV["REDIS_URL"]))
15
+ # config.backend_config = { url: ENV['KAAL_BACKEND_URL'] }
16
+ # config.backend = :redis
16
17
  # end
17
18
  class Configuration
18
19
  # Default values for all configuration options
@@ -23,13 +24,14 @@ module Kaal
23
24
  lease_ttl: 125, # Must be >= window_lookback + tick_interval (120 + 5 = 125)
24
25
  namespace: 'kaal',
25
26
  backend: nil,
27
+ backend_config: {},
26
28
  logger: nil,
27
29
  time_zone: nil,
28
30
  enable_log_dispatch_registry: false,
29
31
  enable_dispatch_recovery: true,
30
32
  recovery_window: 86_400, # 24 hours in seconds
31
33
  recovery_startup_jitter: 5, # max random delay in seconds
32
- scheduler_config_path: 'config/scheduler.yml',
34
+ scheduler_config_path: 'config/kaal-scheduler.yml',
33
35
  scheduler_conflict_policy: :error,
34
36
  scheduler_missing_file_policy: :warn,
35
37
  delayed_job_allowed_class_prefixes: []
@@ -41,6 +43,8 @@ module Kaal
41
43
  # @return [Configuration] a new instance with all defaults set
42
44
  def initialize
43
45
  @values = DEFAULTS.dup
46
+ @backend_name = nil
47
+ @backend_runtime_context = nil
44
48
  end
45
49
 
46
50
  ##
@@ -111,6 +115,7 @@ module Kaal
111
115
  lease_ttl: @values[:lease_ttl],
112
116
  namespace: @values[:namespace],
113
117
  backend: backend&.class&.name,
118
+ backend_config: Kaal::Support::HashTools.deep_dup(@values[:backend_config]),
114
119
  logger: logger&.class&.name,
115
120
  time_zone: @values[:time_zone],
116
121
  enable_log_dispatch_registry: @values[:enable_log_dispatch_registry],
@@ -133,6 +138,7 @@ module Kaal
133
138
  add_window_lookahead_error(errors)
134
139
  add_lease_ttl_error(errors)
135
140
  add_namespace_error(errors)
141
+ add_backend_config_error(errors)
136
142
  add_lease_ttl_window_error(errors)
137
143
  add_scheduler_config_path_error(errors)
138
144
  add_scheduler_conflict_policy_error(errors)
@@ -174,6 +180,12 @@ module Kaal
174
180
  errors << 'namespace cannot be blank'
175
181
  end
176
182
 
183
+ def add_backend_config_error(errors)
184
+ return if @values[:backend_config].is_a?(Hash)
185
+
186
+ errors << 'backend_config must be a hash'
187
+ end
188
+
177
189
  def add_lease_ttl_window_error(errors)
178
190
  lease_ttl = @values[:lease_ttl].to_i
179
191
  window_lookback = @values[:window_lookback].to_i
@@ -224,22 +236,25 @@ module Kaal
224
236
 
225
237
  def set_value(key, value)
226
238
  @values[key] = normalize_value(key, value)
239
+ rebuild_symbolic_backend_if_needed(key)
227
240
  end
228
241
 
229
242
  def normalize_value(key, value)
230
- return value unless @values.key?(key)
231
-
232
243
  case key
244
+ when :backend
245
+ normalize_backend(value)
246
+ when :backend_config
247
+ value.is_a?(Hash) ? Kaal::Support::HashTools.symbolize_keys(Kaal::Support::HashTools.deep_dup(value)) : (value || {})
233
248
  when :tick_interval, :window_lookback, :window_lookahead, :lease_ttl
234
249
  value.to_i
235
250
  when :namespace, :scheduler_config_path
236
251
  value.to_s
237
252
  when :time_zone
238
- value&.to_s
253
+ normalize_optional_string(value)
239
254
  when :enable_log_dispatch_registry
240
- value ? true : false
255
+ !!value
241
256
  when :scheduler_conflict_policy, :scheduler_missing_file_policy
242
- value&.to_sym
257
+ normalize_optional_symbol(value)
243
258
  when :delayed_job_allowed_class_prefixes
244
259
  normalize_delayed_job_allowed_class_prefixes(value)
245
260
  else
@@ -247,12 +262,55 @@ module Kaal
247
262
  end
248
263
  end
249
264
 
265
+ def normalize_backend(value)
266
+ unless value.is_a?(String) || value.is_a?(Symbol)
267
+ @backend_name = nil
268
+ return value
269
+ end
270
+
271
+ normalized_backend_name = Kaal::Config::BackendFactory.normalize_name(value)
272
+ backend = Kaal::Config::BackendFactory.build(
273
+ normalized_backend_name,
274
+ backend_config: @values[:backend_config],
275
+ namespace: @values[:namespace],
276
+ runtime_context: @backend_runtime_context
277
+ )
278
+ @backend_name = normalized_backend_name
279
+ backend
280
+ end
281
+
282
+ def apply_backend_runtime_context(runtime_context)
283
+ @backend_runtime_context = runtime_context
284
+ end
285
+ public :apply_backend_runtime_context
286
+
287
+ def rebuild_symbolic_backend_if_needed(key)
288
+ return unless @backend_name
289
+ return unless %i[backend_config namespace backend].include?(key)
290
+ return if key == :backend
291
+
292
+ @values[:backend] = Kaal::Config::BackendFactory.build(
293
+ @backend_name,
294
+ backend_config: @values[:backend_config],
295
+ namespace: @values[:namespace],
296
+ runtime_context: @backend_runtime_context
297
+ )
298
+ end
299
+
250
300
  def normalize_delayed_job_allowed_class_prefixes(value)
251
301
  Array(value).filter_map do |entry|
252
302
  normalized_entry = entry.to_s.strip
253
303
  normalized_entry unless normalized_entry.empty?
254
304
  end
255
305
  end
306
+
307
+ def normalize_optional_string(value)
308
+ value&.to_s
309
+ end
310
+
311
+ def normalize_optional_symbol(value)
312
+ value&.to_sym
313
+ end
256
314
  end
257
315
 
258
316
  ##