solid_objects 0.10.3 → 0.12.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 +4 -4
- data/CHANGELOG.md +50 -0
- data/README.md +72 -16
- data/app/controllers/solid_objects/components_controller.rb +4 -4
- data/app/models/solid_objects/message.rb +1 -1
- data/app/views/solid_objects/dead_letters/index.html.erb +1 -1
- data/app/views/solid_objects/instances/show.html.erb +1 -1
- data/benchmark/support.rb +10 -10
- data/db/migrate/20260813000000_rename_message_dispatch_columns.rb +53 -0
- data/docs/architecture.md +11 -9
- data/docs/authorization.md +2 -2
- data/docs/database-schema.md +4 -4
- data/docs/development.md +1 -1
- data/docs/migrating-existing-state.md +1 -3
- data/docs/roadmap.md +1 -1
- data/lib/solid_objects/actor.rb +43 -32
- data/lib/solid_objects/actor_channel.rb +3 -3
- data/lib/solid_objects/actor_definition.rb +9 -6
- data/lib/solid_objects/actor_view.rb +1 -1
- data/lib/solid_objects/application_write_guard.rb +1 -1
- data/lib/solid_objects/client.rb +52 -52
- data/lib/solid_objects/component_registration.rb +2 -2
- data/lib/solid_objects/component_renderer.rb +1 -1
- data/lib/solid_objects/component_subscriptions.rb +21 -21
- data/lib/solid_objects/configuration.rb +42 -0
- data/lib/solid_objects/database_adapters/sqlite.rb +7 -1
- data/lib/solid_objects/dead_letter_manager.rb +4 -4
- data/lib/solid_objects/doctor.rb +8 -8
- data/lib/solid_objects/effect_executor.rb +22 -22
- data/lib/solid_objects/errors.rb +27 -27
- data/lib/solid_objects/executor.rb +40 -38
- data/lib/solid_objects/lease.rb +3 -3
- data/lib/solid_objects/mailbox.rb +34 -27
- data/lib/solid_objects/operation_dispatcher.rb +51 -0
- data/lib/solid_objects/payload_broadcast.rb +1 -1
- data/lib/solid_objects/reference.rb +60 -29
- data/lib/solid_objects/reminder_scheduler.rb +6 -6
- data/lib/solid_objects/supervisor.rb +50 -9
- data/lib/solid_objects/sync_diagnostics.rb +6 -6
- data/lib/solid_objects/turbo_stream_renderer.rb +13 -13
- data/lib/solid_objects/version.rb +1 -1
- data/lib/solid_objects.rb +1 -0
- data/sig/generated/controllers/solid_objects/components_controller.rbs +2 -2
- data/sig/generated/lib/solid_objects/actor.rbs +17 -17
- data/sig/generated/lib/solid_objects/actor_definition.rbs +4 -4
- data/sig/generated/lib/solid_objects/client.rbs +8 -8
- data/sig/generated/lib/solid_objects/component_registration.rbs +2 -2
- data/sig/generated/lib/solid_objects/component_subscriptions.rbs +8 -8
- data/sig/generated/lib/solid_objects/configuration.rbs +32 -4
- data/sig/generated/lib/solid_objects/database_adapters/sqlite.rbs +3 -0
- data/sig/generated/lib/solid_objects/effect_executor.rbs +2 -2
- data/sig/generated/lib/solid_objects/errors.rbs +18 -18
- data/sig/generated/lib/solid_objects/executor.rbs +10 -10
- data/sig/generated/lib/solid_objects/lease.rbs +2 -2
- data/sig/generated/lib/solid_objects/mailbox.rbs +8 -8
- data/sig/generated/lib/solid_objects/operation_dispatcher.rbs +35 -0
- data/sig/generated/lib/solid_objects/reference.rbs +7 -4
- data/sig/generated/lib/solid_objects/supervisor.rbs +19 -2
- data/sig/generated/lib/solid_objects/sync_diagnostics.rbs +2 -2
- data/sig/generated/lib/solid_objects/turbo_stream_renderer.rbs +8 -8
- metadata +5 -2
|
@@ -16,41 +16,50 @@ module SolidObjects
|
|
|
16
16
|
freeze
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
-
# @rbs (
|
|
20
|
-
def async(
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
19
|
+
# @rbs (?available_at: Time?, ?idempotency_key: String?, ?authorization_context: untyped) -> OperationDispatcher
|
|
20
|
+
def async(available_at: nil, idempotency_key: nil, authorization_context: nil)
|
|
21
|
+
OperationDispatcher.new(
|
|
22
|
+
actor_type:,
|
|
23
|
+
handlers: actor_definition.messages
|
|
24
|
+
) do |operation, arguments|
|
|
25
|
+
if (actor = Context.current_actor)
|
|
26
|
+
actor.stage_outbound_message(
|
|
27
|
+
reference: self,
|
|
28
|
+
operation:,
|
|
29
|
+
arguments:,
|
|
30
|
+
available_at:,
|
|
31
|
+
idempotency_key:
|
|
32
|
+
)
|
|
33
|
+
next
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
SolidObjects.client.async(
|
|
37
|
+
reference: self,
|
|
38
|
+
operation:,
|
|
39
|
+
arguments:,
|
|
26
40
|
available_at:,
|
|
27
|
-
idempotency_key
|
|
41
|
+
idempotency_key:,
|
|
42
|
+
authorization_context:
|
|
28
43
|
)
|
|
29
|
-
return
|
|
30
44
|
end
|
|
31
|
-
|
|
32
|
-
SolidObjects.client.async(
|
|
33
|
-
self,
|
|
34
|
-
message_name,
|
|
35
|
-
arguments,
|
|
36
|
-
available_at:,
|
|
37
|
-
idempotency_key:,
|
|
38
|
-
authorization_context:
|
|
39
|
-
)
|
|
40
45
|
end
|
|
41
46
|
|
|
42
|
-
# @rbs (
|
|
43
|
-
def sync(
|
|
47
|
+
# @rbs (?timeout: Numeric, ?idempotency_key: String?, ?authorization_context: untyped) -> OperationDispatcher
|
|
48
|
+
def sync(timeout: 5.seconds, idempotency_key: nil, authorization_context: nil)
|
|
44
49
|
raise ActorCallCycle, "actors cannot synchronously wait for another actor" if Context.current_actor
|
|
45
50
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
51
|
+
OperationDispatcher.new(
|
|
52
|
+
actor_type:,
|
|
53
|
+
handlers: actor_definition.messages.merge(actor_definition.queries)
|
|
54
|
+
) do |operation, arguments|
|
|
55
|
+
invoke_synchronously(
|
|
56
|
+
operation,
|
|
57
|
+
arguments,
|
|
58
|
+
timeout:,
|
|
59
|
+
idempotency_key:,
|
|
60
|
+
authorization_context:
|
|
61
|
+
)
|
|
62
|
+
end
|
|
54
63
|
end
|
|
55
64
|
|
|
56
65
|
# @rbs (?authorization_context: untyped) -> bool
|
|
@@ -66,7 +75,15 @@ module SolidObjects
|
|
|
66
75
|
# @rbs (Symbol, *untyped, **untyped) -> untyped
|
|
67
76
|
def method_missing(name, *arguments, **keywords)
|
|
68
77
|
return super if arguments.any? || block_given?
|
|
69
|
-
|
|
78
|
+
if actor_message?(name) || actor_query?(name)
|
|
79
|
+
return invoke_synchronously(
|
|
80
|
+
name,
|
|
81
|
+
keywords,
|
|
82
|
+
timeout: 5.seconds,
|
|
83
|
+
idempotency_key: nil,
|
|
84
|
+
authorization_context: nil
|
|
85
|
+
)
|
|
86
|
+
end
|
|
70
87
|
|
|
71
88
|
super
|
|
72
89
|
end
|
|
@@ -83,6 +100,20 @@ module SolidObjects
|
|
|
83
100
|
|
|
84
101
|
private
|
|
85
102
|
|
|
103
|
+
# @rbs (Symbol | String, Hash[Symbol, untyped], timeout: Numeric, idempotency_key: String?, authorization_context: untyped) -> untyped
|
|
104
|
+
def invoke_synchronously(operation, arguments, timeout:, idempotency_key:, authorization_context:)
|
|
105
|
+
raise ActorCallCycle, "actors cannot synchronously wait for another actor" if Context.current_actor
|
|
106
|
+
|
|
107
|
+
SolidObjects.client.sync(
|
|
108
|
+
reference: self,
|
|
109
|
+
operation:,
|
|
110
|
+
arguments:,
|
|
111
|
+
timeout:,
|
|
112
|
+
idempotency_key:,
|
|
113
|
+
authorization_context:
|
|
114
|
+
)
|
|
115
|
+
end
|
|
116
|
+
|
|
86
117
|
# @rbs (Symbol | String) -> bool
|
|
87
118
|
def actor_message?(name)
|
|
88
119
|
actor_definition.messages.key?(name.to_sym)
|
|
@@ -97,8 +97,8 @@ module SolidObjects
|
|
|
97
97
|
# @rbs (Reminder) -> MessageReference?
|
|
98
98
|
def enqueue(reminder)
|
|
99
99
|
actor_class = SolidObjects.registry.fetch(reminder.actor_type)
|
|
100
|
-
unless actor_class.definition.messages.key?(reminder.
|
|
101
|
-
raise UnknownMessage, "unknown reminder
|
|
100
|
+
unless actor_class.definition.messages.key?(reminder.operation.to_sym)
|
|
101
|
+
raise UnknownMessage, "unknown reminder operation #{reminder.operation.inspect}"
|
|
102
102
|
end
|
|
103
103
|
|
|
104
104
|
mailbox = Mailbox.new(database_adapter:)
|
|
@@ -111,10 +111,10 @@ module SolidObjects
|
|
|
111
111
|
verify_claim!(locked_reminder)
|
|
112
112
|
now = database_adapter.database_now
|
|
113
113
|
message = mailbox.enqueue_in_transaction(
|
|
114
|
-
Reference.new(actor_type: instance.actor_type, actor_id: instance.actor_id),
|
|
115
|
-
locked_reminder.
|
|
116
|
-
locked_reminder.arguments,
|
|
117
|
-
|
|
114
|
+
reference: Reference.new(actor_type: instance.actor_type, actor_id: instance.actor_id),
|
|
115
|
+
operation: locked_reminder.operation,
|
|
116
|
+
arguments: locked_reminder.arguments,
|
|
117
|
+
delivery_mode: "internal",
|
|
118
118
|
idempotency_key: "reminder:#{locked_reminder.id}:#{locked_reminder.occurrence}",
|
|
119
119
|
actor_class:
|
|
120
120
|
)
|
|
@@ -19,12 +19,13 @@ module SolidObjects
|
|
|
19
19
|
broadcast_worker_count: SolidObjects.configuration.broadcast_worker_count,
|
|
20
20
|
reminder_scheduler_count: SolidObjects.configuration.reminder_scheduler_count
|
|
21
21
|
)
|
|
22
|
-
@
|
|
22
|
+
@builders = component_builders(
|
|
23
23
|
worker_count:,
|
|
24
24
|
effect_worker_count:,
|
|
25
25
|
broadcast_worker_count:,
|
|
26
26
|
reminder_scheduler_count:
|
|
27
27
|
)
|
|
28
|
+
@components = build_all(@builders)
|
|
28
29
|
@threads = []
|
|
29
30
|
@monitor = nil
|
|
30
31
|
@started = false
|
|
@@ -77,7 +78,7 @@ module SolidObjects
|
|
|
77
78
|
|
|
78
79
|
private
|
|
79
80
|
|
|
80
|
-
attr_reader :components, :threads
|
|
81
|
+
attr_reader :components, :threads, :builders
|
|
81
82
|
|
|
82
83
|
# A role that raises leaves its thread dead. Without replacement the
|
|
83
84
|
# process keeps running while quietly doing less work, so the supervisor
|
|
@@ -116,7 +117,11 @@ module SolidObjects
|
|
|
116
117
|
replaced = @lifecycle.synchronize do
|
|
117
118
|
next false unless @started
|
|
118
119
|
|
|
119
|
-
|
|
120
|
+
# A component built by this supervisor has a builder, which carries
|
|
121
|
+
# whatever the constructor was given. A component put in place by
|
|
122
|
+
# other means has none, so the class is the only thing left to go on.
|
|
123
|
+
builder = builders[index] || -> { component.class.new }
|
|
124
|
+
replacement = builder.call
|
|
120
125
|
components[index] = replacement
|
|
121
126
|
threads[index] = supervise(replacement)
|
|
122
127
|
replacement
|
|
@@ -250,17 +255,53 @@ module SolidObjects
|
|
|
250
255
|
nil
|
|
251
256
|
end
|
|
252
257
|
|
|
253
|
-
#
|
|
254
|
-
|
|
258
|
+
# A constructor can take a resource, and a later builder can raise. Without
|
|
259
|
+
# this, the components built first would be dropped while still holding
|
|
260
|
+
# whatever they took, and nothing would ever give it back.
|
|
261
|
+
# @rbs (Array[^() -> untyped]) -> Array[untyped]
|
|
262
|
+
def build_all(builders)
|
|
263
|
+
built = []
|
|
264
|
+
builders.each do |builder|
|
|
265
|
+
# The component joins the list before the contract check, so a
|
|
266
|
+
# component that fails the check is stopped along with the rest.
|
|
267
|
+
built << (component = builder.call)
|
|
268
|
+
SolidObjects.configuration.validate_component!(component)
|
|
269
|
+
end
|
|
270
|
+
built
|
|
271
|
+
rescue Exception # rubocop:disable Lint/RescueException
|
|
272
|
+
built.each { |component| stop_after_failed_build(component) }
|
|
273
|
+
raise
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
# The failure that stopped the build is the one worth reporting, so a
|
|
277
|
+
# failure inside the cleanup never replaces it.
|
|
278
|
+
# @rbs (untyped) -> void
|
|
279
|
+
def stop_after_failed_build(component)
|
|
280
|
+
component.stop if component.respond_to?(:stop)
|
|
281
|
+
rescue Exception => error # rubocop:disable Lint/RescueException
|
|
282
|
+
SolidObjects.instrument(
|
|
283
|
+
:"supervisor.component_cleanup_failed",
|
|
284
|
+
role: component.class.name,
|
|
285
|
+
error_class: error.class.name
|
|
286
|
+
)
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
# Each component keeps the builder that made it, so a replacement after a
|
|
290
|
+
# crash is built the same way as the original. Components registered
|
|
291
|
+
# through the configuration run beside the built in ones, under the same
|
|
292
|
+
# supervision, restart, and shutdown timeout.
|
|
293
|
+
# @rbs (worker_count: Integer, effect_worker_count: Integer, broadcast_worker_count: Integer, reminder_scheduler_count: Integer) -> Array[^() -> untyped]
|
|
294
|
+
def component_builders(
|
|
255
295
|
worker_count:,
|
|
256
296
|
effect_worker_count:,
|
|
257
297
|
broadcast_worker_count:,
|
|
258
298
|
reminder_scheduler_count:
|
|
259
299
|
)
|
|
260
|
-
Array.new(worker_count) { Worker.new } +
|
|
261
|
-
Array.new(effect_worker_count) { EffectExecutor.new } +
|
|
262
|
-
Array.new(broadcast_worker_count) { BroadcastExecutor.new } +
|
|
263
|
-
Array.new(reminder_scheduler_count) { ReminderScheduler.new }
|
|
300
|
+
Array.new(worker_count) { -> { Worker.new } } +
|
|
301
|
+
Array.new(effect_worker_count) { -> { EffectExecutor.new } } +
|
|
302
|
+
Array.new(broadcast_worker_count) { -> { BroadcastExecutor.new } } +
|
|
303
|
+
Array.new(reminder_scheduler_count) { -> { ReminderScheduler.new } } +
|
|
304
|
+
SolidObjects.configuration.additional_components
|
|
264
305
|
end
|
|
265
306
|
|
|
266
307
|
# @rbs () -> void
|
|
@@ -8,7 +8,7 @@ module SolidObjects
|
|
|
8
8
|
instance = Instance.uncached { Instance.find(message.instance_id) }
|
|
9
9
|
blocker = earlier_blocker(message)
|
|
10
10
|
status = message_status(message)
|
|
11
|
-
waiting_on = waiting_reason(message
|
|
11
|
+
waiting_on = waiting_reason(message:, instance:, blocker:)
|
|
12
12
|
activation = activation_details(instance)
|
|
13
13
|
build_error(
|
|
14
14
|
message,
|
|
@@ -38,7 +38,7 @@ module SolidObjects
|
|
|
38
38
|
timeout:,
|
|
39
39
|
actor_type: message_reference.actor_type,
|
|
40
40
|
actor_id: message_reference.actor_id,
|
|
41
|
-
|
|
41
|
+
operation: "unknown",
|
|
42
42
|
message_id: message_reference.id,
|
|
43
43
|
request_id: message_reference.request_id,
|
|
44
44
|
sequence: message_reference.sequence,
|
|
@@ -70,7 +70,7 @@ module SolidObjects
|
|
|
70
70
|
timeout:,
|
|
71
71
|
actor_type: message.actor_type,
|
|
72
72
|
actor_id: message.actor_id,
|
|
73
|
-
|
|
73
|
+
operation: message.operation,
|
|
74
74
|
message_id: message.id,
|
|
75
75
|
request_id: message.request_id,
|
|
76
76
|
sequence: message.sequence,
|
|
@@ -105,8 +105,8 @@ module SolidObjects
|
|
|
105
105
|
"unknown"
|
|
106
106
|
end
|
|
107
107
|
|
|
108
|
-
# @rbs (Message, Instance, Message?) -> String
|
|
109
|
-
def waiting_reason(message
|
|
108
|
+
# @rbs (message: Message, instance: Instance, blocker: Message?) -> String
|
|
109
|
+
def waiting_reason(message:, instance:, blocker:)
|
|
110
110
|
return "actor_paused" if instance.paused_at
|
|
111
111
|
return "activation_held" if live_activation?(instance)
|
|
112
112
|
return "earlier_message" if blocker
|
|
@@ -179,7 +179,7 @@ module SolidObjects
|
|
|
179
179
|
{
|
|
180
180
|
"message_id" => blocker.id,
|
|
181
181
|
"sequence" => blocker.sequence,
|
|
182
|
-
"
|
|
182
|
+
"operation" => blocker.operation,
|
|
183
183
|
"status" => message_status(blocker)
|
|
184
184
|
}
|
|
185
185
|
end
|
|
@@ -20,9 +20,9 @@ module SolidObjects
|
|
|
20
20
|
""
|
|
21
21
|
else
|
|
22
22
|
observable_value(
|
|
23
|
-
reference
|
|
24
|
-
broadcast.observable_name,
|
|
25
|
-
broadcast.value
|
|
23
|
+
reference:,
|
|
24
|
+
name: broadcast.observable_name,
|
|
25
|
+
value: broadcast.value
|
|
26
26
|
)
|
|
27
27
|
end
|
|
28
28
|
metadata = Base64.urlsafe_encode64(
|
|
@@ -35,16 +35,16 @@ module SolidObjects
|
|
|
35
35
|
"#{stream}<!--#{INVALIDATION_MARKER}#{metadata}-->"
|
|
36
36
|
end
|
|
37
37
|
|
|
38
|
-
# @rbs (Reference, Symbol | String, untyped) -> String
|
|
39
|
-
def observable_value(reference
|
|
38
|
+
# @rbs (reference: Reference, name: Symbol | String, value: untyped) -> String
|
|
39
|
+
def observable_value(reference:, name:, value:)
|
|
40
40
|
target = DomIdentity.observable(reference, name)
|
|
41
41
|
content = ERB::Util.html_escape(display_value(value))
|
|
42
42
|
%(<turbo-stream action="replace" target="#{target}"><template><span id="#{target}">#{content}</span></template></turbo-stream>)
|
|
43
43
|
end
|
|
44
44
|
|
|
45
|
-
# @rbs (ComponentRegistration, Integer, Integer) -> String
|
|
46
|
-
def component_refresh(registration
|
|
47
|
-
return morph_component_refresh(registration
|
|
45
|
+
# @rbs (registration: ComponentRegistration, instance_id: Integer, revision: Integer) -> String
|
|
46
|
+
def component_refresh(registration:, instance_id:, revision:)
|
|
47
|
+
return morph_component_refresh(registration:, instance_id:, revision:) if registration.morph?
|
|
48
48
|
|
|
49
49
|
target = registration.dom_id
|
|
50
50
|
source = ERB::Util.html_escape(
|
|
@@ -54,13 +54,13 @@ module SolidObjects
|
|
|
54
54
|
%(<turbo-stream action="replace" target="#{target}"><template><turbo-frame id="#{target}" src="#{source}" data-solid-objects-revision="#{revision_value}" data-solid-objects-refresh="replace"></turbo-frame></template></turbo-stream>)
|
|
55
55
|
end
|
|
56
56
|
|
|
57
|
-
# @rbs (Array[ComponentRegistration], Integer, Integer) -> String
|
|
58
|
-
def batch_refresh(registrations
|
|
57
|
+
# @rbs (registrations: Array[ComponentRegistration], instance_id: Integer, revision: Integer) -> String
|
|
58
|
+
def batch_refresh(registrations:, instance_id:, revision:)
|
|
59
59
|
first = registrations.first
|
|
60
60
|
scope = DomIdentity.scope(first.reference)
|
|
61
61
|
batch = ERB::Util.html_escape(first.batch)
|
|
62
62
|
source = ERB::Util.html_escape(
|
|
63
|
-
first.batch_refresh_url(registrations
|
|
63
|
+
first.batch_refresh_url(registrations:, instance_id:, revision:)
|
|
64
64
|
)
|
|
65
65
|
targets = ERB::Util.html_escape(registrations.map(&:dom_id).join(" "))
|
|
66
66
|
%(<turbo-stream action="append" target="#{scope}"><template><solid-objects-batch-refresh data-batch="#{batch}" data-revision="#{instance_id}:#{revision}" data-targets="#{targets}" data-source="#{source}"></solid-objects-batch-refresh></template></turbo-stream>)
|
|
@@ -104,8 +104,8 @@ module SolidObjects
|
|
|
104
104
|
end
|
|
105
105
|
private_class_method :display_value
|
|
106
106
|
|
|
107
|
-
# @rbs (ComponentRegistration, Integer, Integer) -> String
|
|
108
|
-
def morph_component_refresh(registration
|
|
107
|
+
# @rbs (registration: ComponentRegistration, instance_id: Integer, revision: Integer) -> String
|
|
108
|
+
def morph_component_refresh(registration:, instance_id:, revision:)
|
|
109
109
|
source = ERB::Util.html_escape(
|
|
110
110
|
registration.refresh_url(instance_id, revision)
|
|
111
111
|
)
|
data/lib/solid_objects.rb
CHANGED
|
@@ -21,6 +21,7 @@ require "solid_objects/serialization"
|
|
|
21
21
|
require "solid_objects/context"
|
|
22
22
|
require "solid_objects/actor_registry"
|
|
23
23
|
require "solid_objects/state"
|
|
24
|
+
require "solid_objects/operation_dispatcher"
|
|
24
25
|
require "solid_objects/actor_definition"
|
|
25
26
|
require "solid_objects/application_write_guard"
|
|
26
27
|
require "solid_objects/actor"
|
|
@@ -39,7 +39,7 @@ module SolidObjects
|
|
|
39
39
|
# @rbs () -> untyped
|
|
40
40
|
def component_view_context: () -> untyped
|
|
41
41
|
|
|
42
|
-
# @rbs (ComponentRegistration, ActorSnapshot, untyped) -> String
|
|
43
|
-
def component_frame: (ComponentRegistration, ActorSnapshot, untyped) -> String
|
|
42
|
+
# @rbs (registration: ComponentRegistration, snapshot: ActorSnapshot, rendered: untyped) -> String
|
|
43
|
+
def component_frame: (registration: ComponentRegistration, snapshot: ActorSnapshot, rendered: untyped) -> String
|
|
44
44
|
end
|
|
45
45
|
end
|
|
@@ -7,16 +7,16 @@ module SolidObjects
|
|
|
7
7
|
|
|
8
8
|
attr_reader arguments(): untyped
|
|
9
9
|
|
|
10
|
-
attr_reader
|
|
10
|
+
attr_reader success_operation(): untyped
|
|
11
11
|
|
|
12
|
-
attr_reader
|
|
12
|
+
attr_reader failure_operation(): untyped
|
|
13
13
|
|
|
14
|
-
def self.new: (untyped name, untyped arguments, untyped
|
|
15
|
-
| (name: untyped, arguments: untyped,
|
|
14
|
+
def self.new: (untyped name, untyped arguments, untyped success_operation, untyped failure_operation) -> instance
|
|
15
|
+
| (name: untyped, arguments: untyped, success_operation: untyped, failure_operation: untyped) -> instance
|
|
16
16
|
|
|
17
|
-
def self.members: () -> [ :name, :arguments, :
|
|
17
|
+
def self.members: () -> [ :name, :arguments, :success_operation, :failure_operation ]
|
|
18
18
|
|
|
19
|
-
def members: () -> [ :name, :arguments, :
|
|
19
|
+
def members: () -> [ :name, :arguments, :success_operation, :failure_operation ]
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
class CommitActionIntent < Data
|
|
@@ -56,7 +56,7 @@ module SolidObjects
|
|
|
56
56
|
|
|
57
57
|
attr_reader actor_id(): untyped
|
|
58
58
|
|
|
59
|
-
attr_reader
|
|
59
|
+
attr_reader operation(): untyped
|
|
60
60
|
|
|
61
61
|
attr_reader arguments(): untyped
|
|
62
62
|
|
|
@@ -64,12 +64,12 @@ module SolidObjects
|
|
|
64
64
|
|
|
65
65
|
attr_reader idempotency_key(): untyped
|
|
66
66
|
|
|
67
|
-
def self.new: (untyped actor_type, untyped actor_id, untyped
|
|
68
|
-
| (actor_type: untyped, actor_id: untyped,
|
|
67
|
+
def self.new: (untyped actor_type, untyped actor_id, untyped operation, untyped arguments, untyped available_at, untyped idempotency_key) -> instance
|
|
68
|
+
| (actor_type: untyped, actor_id: untyped, operation: untyped, arguments: untyped, available_at: untyped, idempotency_key: untyped) -> instance
|
|
69
69
|
|
|
70
|
-
def self.members: () -> [ :actor_type, :actor_id, :
|
|
70
|
+
def self.members: () -> [ :actor_type, :actor_id, :operation, :arguments, :available_at, :idempotency_key ]
|
|
71
71
|
|
|
72
|
-
def members: () -> [ :actor_type, :actor_id, :
|
|
72
|
+
def members: () -> [ :actor_type, :actor_id, :operation, :arguments, :available_at, :idempotency_key ]
|
|
73
73
|
end
|
|
74
74
|
|
|
75
75
|
# @rbs (Class) -> void
|
|
@@ -157,11 +157,11 @@ module SolidObjects
|
|
|
157
157
|
# @rbs (Symbol | String, **untyped) -> nil
|
|
158
158
|
def commit_action: (Symbol | String, **untyped) -> nil
|
|
159
159
|
|
|
160
|
-
# @rbs (
|
|
161
|
-
def schedule: (
|
|
160
|
+
# @rbs (at: Time, ?every: Numeric?, ?missed: Symbol | String) -> OperationDispatcher
|
|
161
|
+
def schedule: (at: Time, ?every: Numeric?, ?missed: Symbol | String) -> OperationDispatcher
|
|
162
162
|
|
|
163
|
-
# @rbs (Reference,
|
|
164
|
-
def send_to: (Reference,
|
|
163
|
+
# @rbs (Reference, ?available_at: Time?, ?idempotency_key: String?) -> OperationDispatcher
|
|
164
|
+
def send_to: (Reference, ?available_at: Time?, ?idempotency_key: String?) -> OperationDispatcher
|
|
165
165
|
|
|
166
166
|
# @rbs (Symbol | String, Hash[String, untyped]) -> untyped
|
|
167
167
|
def invoke: (Symbol | String, Hash[String, untyped]) -> untyped
|
|
@@ -178,8 +178,8 @@ module SolidObjects
|
|
|
178
178
|
# @rbs () -> void
|
|
179
179
|
def deactivate: () -> void
|
|
180
180
|
|
|
181
|
-
# @rbs (Reference, Symbol | String, Hash[Symbol | String, untyped], ?available_at: Time?, idempotency_key: String?) -> OutboundMessageIntent
|
|
182
|
-
def stage_outbound_message: (Reference, Symbol | String, Hash[Symbol | String, untyped], idempotency_key: String?, ?available_at: Time?) -> OutboundMessageIntent
|
|
181
|
+
# @rbs (reference: Reference, operation: Symbol | String, arguments: Hash[Symbol | String, untyped], ?available_at: Time?, idempotency_key: String?) -> OutboundMessageIntent
|
|
182
|
+
def stage_outbound_message: (reference: Reference, operation: Symbol | String, arguments: Hash[Symbol | String, untyped], idempotency_key: String?, ?available_at: Time?) -> OutboundMessageIntent
|
|
183
183
|
|
|
184
184
|
# @rbs () -> Array[EffectIntent]
|
|
185
185
|
def drain_effect_intents: () -> Array[EffectIntent]
|
|
@@ -94,8 +94,8 @@ module SolidObjects
|
|
|
94
94
|
# @rbs (Integer) -> Integer
|
|
95
95
|
def set_state_version: (Integer) -> Integer
|
|
96
96
|
|
|
97
|
-
# @rbs (Integer, Integer, Proc) -> StateMigration
|
|
98
|
-
def add_state_migration: (Integer, Integer, Proc) -> StateMigration
|
|
97
|
+
# @rbs (from: Integer, to: Integer, block: Proc) -> StateMigration
|
|
98
|
+
def add_state_migration: (from: Integer, to: Integer, block: Proc) -> StateMigration
|
|
99
99
|
|
|
100
100
|
# @rbs (Integer, Hash[String, untyped]) -> Hash[String, untyped]
|
|
101
101
|
def migrate_state: (Integer, Hash[String, untyped]) -> Hash[String, untyped]
|
|
@@ -118,7 +118,7 @@ module SolidObjects
|
|
|
118
118
|
# @rbs (Array[Symbol]) -> void
|
|
119
119
|
def validate_attribute_methods!: (Array[Symbol]) -> void
|
|
120
120
|
|
|
121
|
-
# @rbs (Hash[Symbol, Handler], Symbol | String, Proc) -> Handler
|
|
122
|
-
def add_handler: (Hash[Symbol, Handler], Symbol | String, Proc) -> Handler
|
|
121
|
+
# @rbs (collection: Hash[Symbol, Handler], name: Symbol | String, block: Proc) -> Handler
|
|
122
|
+
def add_handler: (collection: Hash[Symbol, Handler], name: Symbol | String, block: Proc) -> Handler
|
|
123
123
|
end
|
|
124
124
|
end
|
|
@@ -7,11 +7,11 @@ module SolidObjects
|
|
|
7
7
|
# @rbs (?mailbox: Mailbox) -> void
|
|
8
8
|
def initialize: (?mailbox: Mailbox) -> void
|
|
9
9
|
|
|
10
|
-
# @rbs (Reference, Symbol | String, Hash[Symbol | String, untyped], ?available_at: Time?, ?idempotency_key: String?, ?authorization_context: untyped) -> MessageReference
|
|
11
|
-
def async: (Reference, Symbol | String, Hash[Symbol | String, untyped], ?available_at: Time?, ?idempotency_key: String?, ?authorization_context: untyped) -> MessageReference
|
|
10
|
+
# @rbs (reference: Reference, operation: Symbol | String, arguments: Hash[Symbol | String, untyped], ?available_at: Time?, ?idempotency_key: String?, ?authorization_context: untyped) -> MessageReference
|
|
11
|
+
def async: (reference: Reference, operation: Symbol | String, arguments: Hash[Symbol | String, untyped], ?available_at: Time?, ?idempotency_key: String?, ?authorization_context: untyped) -> MessageReference
|
|
12
12
|
|
|
13
|
-
# @rbs (Reference, Symbol | String, Hash[Symbol | String, untyped], timeout: Numeric, ?idempotency_key: String?, ?authorization_context: untyped) -> untyped
|
|
14
|
-
def sync: (Reference, Symbol | String, Hash[Symbol | String, untyped], timeout: Numeric, ?idempotency_key: String?, ?authorization_context: untyped) -> untyped
|
|
13
|
+
# @rbs (reference: Reference, operation: Symbol | String, arguments: Hash[Symbol | String, untyped], timeout: Numeric, ?idempotency_key: String?, ?authorization_context: untyped) -> untyped
|
|
14
|
+
def sync: (reference: Reference, operation: Symbol | String, arguments: Hash[Symbol | String, untyped], timeout: Numeric, ?idempotency_key: String?, ?authorization_context: untyped) -> untyped
|
|
15
15
|
|
|
16
16
|
# @rbs (MessageReference, timeout: Numeric, ?authorization_context: untyped) -> untyped
|
|
17
17
|
def wait: (MessageReference, timeout: Numeric, ?authorization_context: untyped) -> untyped
|
|
@@ -26,8 +26,8 @@ module SolidObjects
|
|
|
26
26
|
|
|
27
27
|
attr_reader mailbox: untyped
|
|
28
28
|
|
|
29
|
-
# @rbs (Reference, Symbol | String, Hash[Symbol | String, untyped], idempotency_key: String?, timeout: Numeric) -> MessageReference
|
|
30
|
-
def enqueue_sync: (Reference, Symbol | String, Hash[Symbol | String, untyped], idempotency_key: String?, timeout: Numeric) -> MessageReference
|
|
29
|
+
# @rbs (reference: Reference, operation: Symbol | String, arguments: Hash[Symbol | String, untyped], idempotency_key: String?, timeout: Numeric) -> MessageReference
|
|
30
|
+
def enqueue_sync: (reference: Reference, operation: Symbol | String, arguments: Hash[Symbol | String, untyped], idempotency_key: String?, timeout: Numeric) -> MessageReference
|
|
31
31
|
|
|
32
32
|
# @rbs (MessageReference, Message) -> void
|
|
33
33
|
def validate_message_reference!: (MessageReference, Message) -> void
|
|
@@ -35,8 +35,8 @@ module SolidObjects
|
|
|
35
35
|
# @rbs (Reference, Symbol | String) -> void
|
|
36
36
|
def reject_sync_inside_transaction!: (Reference, Symbol | String) -> void
|
|
37
37
|
|
|
38
|
-
# @rbs (Proc, Reference, Symbol | String, Hash[Symbol | String, untyped], authorization_context: untyped) -> void
|
|
39
|
-
def authorize!: (Proc, Reference, Symbol | String, Hash[Symbol | String, untyped], authorization_context: untyped) -> void
|
|
38
|
+
# @rbs (hook: Proc, reference: Reference, operation: Symbol | String, arguments: Hash[Symbol | String, untyped], authorization_context: untyped) -> void
|
|
39
|
+
def authorize!: (hook: Proc, reference: Reference, operation: Symbol | String, arguments: Hash[Symbol | String, untyped], authorization_context: untyped) -> void
|
|
40
40
|
|
|
41
41
|
# @rbs (Reference, authorization_context: untyped) -> void
|
|
42
42
|
def authorize_destroy!: (Reference, authorization_context: untyped) -> void
|
|
@@ -73,8 +73,8 @@ module SolidObjects
|
|
|
73
73
|
# @rbs () -> Hash[String, untyped]
|
|
74
74
|
def authorization_arguments: () -> Hash[String, untyped]
|
|
75
75
|
|
|
76
|
-
# @rbs (Array[ComponentRegistration], Integer, Integer) -> String
|
|
77
|
-
def batch_refresh_url: (Array[ComponentRegistration], Integer, Integer) -> String
|
|
76
|
+
# @rbs (registrations: Array[ComponentRegistration], instance_id: Integer, revision: Integer) -> String
|
|
77
|
+
def batch_refresh_url: (registrations: Array[ComponentRegistration], instance_id: Integer, revision: Integer) -> String
|
|
78
78
|
|
|
79
79
|
# @rbs (Integer, Integer) -> String
|
|
80
80
|
def refresh_url: (Integer, Integer) -> String
|
|
@@ -33,16 +33,16 @@ module SolidObjects
|
|
|
33
33
|
|
|
34
34
|
# Live invalidations and reconnect replays share this, so a reconnecting
|
|
35
35
|
# client pays the same number of requests a connected one does.
|
|
36
|
-
# @rbs (Array[ComponentRegistration], Integer, Integer) -> Array[String]
|
|
37
|
-
def refresh_streams: (Array[ComponentRegistration], Integer, Integer) -> Array[String]
|
|
36
|
+
# @rbs (changed: Array[ComponentRegistration], instance_id: Integer, revision: Integer) -> Array[String]
|
|
37
|
+
def refresh_streams: (changed: Array[ComponentRegistration], instance_id: Integer, revision: Integer) -> Array[String]
|
|
38
38
|
|
|
39
|
-
# @rbs (ComponentRegistration, Integer, Integer) -> void
|
|
40
|
-
def record_revision: (ComponentRegistration, Integer, Integer) -> void
|
|
39
|
+
# @rbs (registration: ComponentRegistration, instance_id: Integer, revision: Integer) -> void
|
|
40
|
+
def record_revision: (registration: ComponentRegistration, instance_id: Integer, revision: Integer) -> void
|
|
41
41
|
|
|
42
|
-
# @rbs (ComponentRegistration, Integer, Integer) -> String
|
|
43
|
-
def refresh: (ComponentRegistration, Integer, Integer) -> String
|
|
42
|
+
# @rbs (registration: ComponentRegistration, instance_id: Integer, revision: Integer) -> String
|
|
43
|
+
def refresh: (registration: ComponentRegistration, instance_id: Integer, revision: Integer) -> String
|
|
44
44
|
|
|
45
|
-
# @rbs (String, Integer, Integer) -> bool
|
|
46
|
-
def newer_revision?: (String, Integer, Integer) -> bool
|
|
45
|
+
# @rbs (dom_id: String, instance_id: Integer, revision: Integer) -> bool
|
|
46
|
+
def newer_revision?: (dom_id: String, instance_id: Integer, revision: Integer) -> bool
|
|
47
47
|
end
|
|
48
48
|
end
|
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
module SolidObjects
|
|
4
4
|
class Configuration
|
|
5
|
+
@process_alive_threshold: Float
|
|
6
|
+
|
|
7
|
+
@shutdown_timeout: Float
|
|
8
|
+
|
|
5
9
|
@supervisor_monitor_interval: Float
|
|
6
10
|
|
|
7
11
|
@retention_interval: Float
|
|
@@ -86,10 +90,6 @@ module SolidObjects
|
|
|
86
90
|
|
|
87
91
|
@process_heartbeat_interval: Float
|
|
88
92
|
|
|
89
|
-
@process_alive_threshold: Float
|
|
90
|
-
|
|
91
|
-
@shutdown_timeout: Float
|
|
92
|
-
|
|
93
93
|
attr_accessor table_name_prefix: untyped
|
|
94
94
|
|
|
95
95
|
attr_accessor polling_interval: untyped
|
|
@@ -178,9 +178,37 @@ module SolidObjects
|
|
|
178
178
|
|
|
179
179
|
attr_accessor authorize_administration: untyped
|
|
180
180
|
|
|
181
|
+
# @rbs @additional_components: Array[untyped]
|
|
182
|
+
attr_reader additional_components: untyped
|
|
183
|
+
|
|
181
184
|
# @rbs () -> void
|
|
182
185
|
def initialize: () -> void
|
|
183
186
|
|
|
187
|
+
# Registers a long running component that the supervisor runs beside its own
|
|
188
|
+
# workers. An extension gem uses this to share one process, rather than ask
|
|
189
|
+
# an operator to run and monitor a second one.
|
|
190
|
+
#
|
|
191
|
+
# The block must return an object that answers `run`, `request_shutdown`,
|
|
192
|
+
# `stopped?`, and `stop`, which is the contract the built in components
|
|
193
|
+
# already keep. The supervisor calls the block once for each supervisor it
|
|
194
|
+
# builds, and again when it replaces a crashed component, so two
|
|
195
|
+
# supervisors never share one component instance.
|
|
196
|
+
#
|
|
197
|
+
# @rbs (?count: Integer) { () -> untyped } -> void
|
|
198
|
+
def register_component: (?count: Integer) { () -> untyped } -> void
|
|
199
|
+
|
|
200
|
+
# The supervisor checks the contract here rather than at registration,
|
|
201
|
+
# because a component often needs a database connection to exist, and
|
|
202
|
+
# registration happens while the application boots.
|
|
203
|
+
# @rbs () -> Array[untyped]
|
|
204
|
+
def build_additional_components: () -> Array[untyped]
|
|
205
|
+
|
|
206
|
+
# A component that misses part of the contract would hang the supervisor at
|
|
207
|
+
# shutdown, or crash the moment it starts. The build fails instead, where
|
|
208
|
+
# the caller can read the reason.
|
|
209
|
+
# @rbs (untyped) -> void
|
|
210
|
+
def validate_component!: (untyped) -> void
|
|
211
|
+
|
|
184
212
|
# @rbs () -> self
|
|
185
213
|
def validate!: () -> self
|
|
186
214
|
|
|
@@ -76,7 +76,7 @@ module SolidObjects
|
|
|
76
76
|
# @rbs (Effect) -> void
|
|
77
77
|
def verify_claim!: (Effect) -> void
|
|
78
78
|
|
|
79
|
-
# @rbs (Effect, String?, String, Hash[String, untyped]) -> Message?
|
|
80
|
-
def enqueue_result_message: (Effect, String?, String, Hash[String, untyped]) -> Message?
|
|
79
|
+
# @rbs (effect: Effect, operation: String?, outcome: String, arguments: Hash[String, untyped]) -> Message?
|
|
80
|
+
def enqueue_result_message: (effect: Effect, operation: String?, outcome: String, arguments: Hash[String, untyped]) -> Message?
|
|
81
81
|
end
|
|
82
82
|
end
|