legionio 1.7.13 → 1.7.14
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 +4 -4
- data/CHANGELOG.md +8 -0
- data/lib/legion/extensions/builders/actors.rb +5 -0
- data/lib/legion/extensions/catalog.rb +46 -17
- data/lib/legion/extensions.rb +38 -9
- data/lib/legion/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 86fc5b0df46813866069443960f455bfd54c68eab894e96e23c763ef1c3356e2
|
|
4
|
+
data.tar.gz: f3881012c84c5eaf039ae0c46243e9a97d5aa2ef53092a5b733a1cdeeacf9d08
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f191aabb91a76ac66d6d71890c2fcdc273f4cbd60f77768a024064b8f58386a6e2791073364c6deb93dfd95b5a8fb8485b0b26c7d0dad67eb5c3d40a1c337289
|
|
7
|
+
data.tar.gz: bb61732fe43b41f23e8bb96defb087c206d5d397bd165e3c6eef5c16f9b12e542f88de846639fb767300c5050960f0b7298d83be3d720ab80d5d11096a1008ae
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [1.7.14] - 2026-04-03
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
- Actor boot ordering: once → poll → every → loop → subscriptions, preventing timer actors from competing with AMQP channel setup
|
|
9
|
+
- Builder now respects `remote_invocable? false` and skips auto-generated subscription actors for local-only extensions
|
|
10
|
+
- Catalog exchange cached and reused instead of creating a new channel + exchange_declare per transition
|
|
11
|
+
- Catalog SQLite persists batched into a single transaction at end of boot instead of per-transition writes from concurrent threads
|
|
12
|
+
|
|
5
13
|
## [1.7.13] - 2026-04-03
|
|
6
14
|
|
|
7
15
|
### Changed
|
|
@@ -37,6 +37,11 @@ module Legion
|
|
|
37
37
|
end
|
|
38
38
|
|
|
39
39
|
def build_meta_actor_list
|
|
40
|
+
if lex_class.respond_to?(:remote_invocable?) && !lex_class.remote_invocable?
|
|
41
|
+
log.debug "[Actors] skipping meta actors for #{lex_class} (remote_invocable=false)"
|
|
42
|
+
return
|
|
43
|
+
end
|
|
44
|
+
|
|
40
45
|
@runners.each do |runner, attr|
|
|
41
46
|
next if @actors[runner.to_sym].is_a? Hash
|
|
42
47
|
|
|
@@ -61,6 +61,40 @@ module Legion
|
|
|
61
61
|
@warned_missing_extension_catalog = false
|
|
62
62
|
end
|
|
63
63
|
|
|
64
|
+
def flush_persisted_transitions
|
|
65
|
+
pending = nil
|
|
66
|
+
@pending_persists_mutex ||= Mutex.new
|
|
67
|
+
@pending_persists_mutex.synchronize do
|
|
68
|
+
return if @pending_persists.nil? || @pending_persists.empty?
|
|
69
|
+
|
|
70
|
+
pending = @pending_persists.dup
|
|
71
|
+
@pending_persists.clear
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
return unless defined?(Legion::Data::Local) &&
|
|
75
|
+
Legion::Data::Local.respond_to?(:connected?) &&
|
|
76
|
+
Legion::Data::Local.connected?
|
|
77
|
+
|
|
78
|
+
ensure_local_migration_registered!
|
|
79
|
+
return warn_missing_extension_catalog_once unless extension_catalog_table_available?
|
|
80
|
+
|
|
81
|
+
model = Legion::Data::Local.model(:extension_catalog)
|
|
82
|
+
now = Time.now
|
|
83
|
+
Legion::Data::Local.connection.transaction do
|
|
84
|
+
pending.each do |lex_name, new_state|
|
|
85
|
+
existing = model.where(lex_name: lex_name).first
|
|
86
|
+
if existing
|
|
87
|
+
existing.update(state: new_state.to_s, updated_at: now)
|
|
88
|
+
else
|
|
89
|
+
model.insert(lex_name: lex_name, state: new_state.to_s, created_at: now, updated_at: now)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
Legion::Logging.info "Catalog persisted #{pending.size} transitions" if defined?(Legion::Logging)
|
|
94
|
+
rescue StandardError => e
|
|
95
|
+
Legion::Logging.warn { "Catalog flush failed: #{e.class}: #{e.message}" } if defined?(Legion::Logging)
|
|
96
|
+
end
|
|
97
|
+
|
|
64
98
|
private
|
|
65
99
|
|
|
66
100
|
def entries
|
|
@@ -78,30 +112,25 @@ module Legion
|
|
|
78
112
|
timestamp: Time.now.to_i
|
|
79
113
|
)
|
|
80
114
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
content_type: 'application/json', persistent: true)
|
|
115
|
+
catalog_exchange.publish(payload, routing_key: "legion.catalog.#{lex_name}.#{new_state}",
|
|
116
|
+
content_type: 'application/json', persistent: true)
|
|
84
117
|
rescue StandardError => e
|
|
118
|
+
@catalog_exchange = nil
|
|
85
119
|
Legion::Logging.warn { "Catalog publish failed for #{lex_name}=#{new_state}: #{e.class}: #{e.message}" } if defined?(Legion::Logging)
|
|
86
120
|
end
|
|
87
121
|
|
|
88
|
-
def
|
|
89
|
-
return
|
|
90
|
-
Legion::Data::Local.respond_to?(:connected?) &&
|
|
91
|
-
Legion::Data::Local.connected?
|
|
122
|
+
def catalog_exchange
|
|
123
|
+
return @catalog_exchange if @catalog_exchange&.channel&.open?
|
|
92
124
|
|
|
93
|
-
|
|
94
|
-
|
|
125
|
+
@catalog_exchange = Legion::Transport::Exchange.new('legion.catalog')
|
|
126
|
+
end
|
|
95
127
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
model.insert(lex_name: lex_name, state: new_state.to_s, created_at: Time.now, updated_at: Time.now)
|
|
128
|
+
def persist_transition(lex_name, new_state)
|
|
129
|
+
@pending_persists_mutex ||= Mutex.new
|
|
130
|
+
@pending_persists_mutex.synchronize do
|
|
131
|
+
@pending_persists ||= {}
|
|
132
|
+
@pending_persists[lex_name] = new_state
|
|
102
133
|
end
|
|
103
|
-
rescue StandardError => e
|
|
104
|
-
Legion::Logging.warn { "Catalog persist failed for #{lex_name}=#{new_state}: #{e.class}: #{e.message}" } if defined?(Legion::Logging)
|
|
105
134
|
end
|
|
106
135
|
|
|
107
136
|
def extension_catalog_table_available?
|
data/lib/legion/extensions.rb
CHANGED
|
@@ -279,16 +279,18 @@ module Legion
|
|
|
279
279
|
|
|
280
280
|
Legion::Logging.info "Hooking #{@pending_actors.size} deferred actors"
|
|
281
281
|
|
|
282
|
-
|
|
283
|
-
@pending_actors.each do |actor|
|
|
284
|
-
if actor[:actor_class].ancestors.include?(Legion::Extensions::Actors::Subscription)
|
|
285
|
-
sub_actors << actor
|
|
286
|
-
else
|
|
287
|
-
hook_actor(**actor)
|
|
288
|
-
end
|
|
289
|
-
end
|
|
282
|
+
groups = group_pending_actors
|
|
290
283
|
|
|
291
|
-
|
|
284
|
+
%i[once poll every loop].each do |type|
|
|
285
|
+
next if groups[type].empty?
|
|
286
|
+
|
|
287
|
+
Legion::Logging.info "Starting #{type} actors (#{groups[type].size})"
|
|
288
|
+
groups[type].each { |actor| hook_actor(**actor) }
|
|
289
|
+
end
|
|
290
|
+
unless groups[:subscription].empty?
|
|
291
|
+
Legion::Logging.info "Starting subscription actors (#{groups[:subscription].size})"
|
|
292
|
+
hook_subscription_actors_pooled(groups[:subscription])
|
|
293
|
+
end
|
|
292
294
|
dispatch_local_actors(@local_tasks) unless @local_tasks.empty?
|
|
293
295
|
|
|
294
296
|
@pending_actors.clear
|
|
@@ -301,6 +303,33 @@ module Legion
|
|
|
301
303
|
"local:#{@local_tasks.count}"
|
|
302
304
|
)
|
|
303
305
|
@loaded_extensions&.each { |name| Catalog.transition(name, :running) }
|
|
306
|
+
Catalog.flush_persisted_transitions
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
ACTOR_TYPE_MAP = {
|
|
310
|
+
Once: :once,
|
|
311
|
+
Poll: :poll,
|
|
312
|
+
Every: :every,
|
|
313
|
+
Loop: :loop,
|
|
314
|
+
Subscription: :subscription
|
|
315
|
+
}.freeze
|
|
316
|
+
|
|
317
|
+
def group_pending_actors
|
|
318
|
+
groups = { once: [], poll: [], every: [], loop: [], subscription: [] }
|
|
319
|
+
@pending_actors.each do |actor|
|
|
320
|
+
type = resolve_actor_type(actor[:actor_class])
|
|
321
|
+
groups[type] << actor
|
|
322
|
+
end
|
|
323
|
+
groups
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
def resolve_actor_type(actor_class)
|
|
327
|
+
anc = actor_class.ancestors
|
|
328
|
+
ACTOR_TYPE_MAP.each do |const, type|
|
|
329
|
+
return type if anc.include?(Legion::Extensions::Actors.const_get(const))
|
|
330
|
+
end
|
|
331
|
+
Legion::Logging.warn "Unknown actor type for #{actor_class}, defaulting to loop"
|
|
332
|
+
:loop
|
|
304
333
|
end
|
|
305
334
|
|
|
306
335
|
def hook_actor(extension:, extension_name:, actor_class:, size: 1, **opts)
|
data/lib/legion/version.rb
CHANGED