Skip to content

Commit d12b2e2

Browse files
committed
fix: keep the instance when a turn restores an actor
restore_state rebuilt the actor after a rejected or failed turn without the instance id, so the activation kept draining with an actor that read every committed reminder as absent. The activation caches that actor, so one failed turn poisoned every later read in the same pass. The three construction sites were the cause, so there is now one. Activation holds the actor id and the instance id from the start, and both build_actor and restore_state go through new_actor. A fourth site cannot forget what it never passes.
1 parent a814d46 commit d12b2e2

3 files changed

Lines changed: 41 additions & 6 deletions

File tree

‎lib/solid_objects/activation.rb‎

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
module SolidObjects
44
class Activation
55
# @rbs @lease: Lease
6+
# @rbs @actor_id: String
7+
# @rbs @instance_id: Integer
68
# @rbs @actor_class: Class
79
# @rbs @actor: Actor
810
# @rbs @last_used_at: Float
@@ -17,6 +19,8 @@ def initialize(lease:)
1719
Instance.find(lease.instance_id)
1820
end
1921
@actor_class = SolidObjects.registry.fetch(instance.actor_type)
22+
@actor_id = instance.actor_id
23+
@instance_id = instance.id
2024
@actor = build_actor(instance)
2125
@last_used_at = monotonic_now
2226
@pass_exhausted = false
@@ -86,10 +90,7 @@ def yield_ready_messages
8690

8791
# @rbs (Hash[String, untyped]) -> void
8892
def restore_state(state_data)
89-
@actor = actor_class.new(
90-
actor_id: actor.actor_id,
91-
state: State.new(actor_class.definition.state_definition, state_data)
92-
)
93+
@actor = new_actor(state_data)
9394
end
9495

9596
# @rbs () -> void
@@ -156,10 +157,15 @@ def build_actor(instance)
156157
) do
157158
actor_class.definition.migrate_state(instance.state_version, instance.state)
158159
end
160+
new_actor(state_data)
161+
end
162+
163+
# @rbs (Hash[String, untyped]) -> Actor
164+
def new_actor(state_data)
159165
actor_class.new(
160-
actor_id: instance.actor_id,
166+
actor_id: @actor_id,
161167
state: State.new(actor_class.definition.state_definition, state_data),
162-
instance_id: instance.id
168+
instance_id: @instance_id
163169
)
164170
end
165171

‎sig/generated/lib/solid_objects/activation.rbs‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ module SolidObjects
1010

1111
@actor_class: Class
1212

13+
@instance_id: Integer
14+
15+
@actor_id: String
16+
1317
@lease: Lease
1418

1519
attr_reader lease: untyped
@@ -59,6 +63,9 @@ module SolidObjects
5963
# @rbs (Instance) -> Actor
6064
def build_actor: (Instance) -> Actor
6165

66+
# @rbs (Hash[String, untyped]) -> Actor
67+
def new_actor: (Hash[String, untyped]) -> Actor
68+
6269
# @rbs () -> void
6370
def release_lease: () -> void
6471

‎test/integration/reminder_cancellation_test.rb‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,14 @@ def arm
100100
schedule(at: Time.utc(2030, 1, 1)).ping
101101
end
102102

103+
def fail_turn
104+
raise "turn failed"
105+
end
106+
107+
def record_seen
108+
self.seen_on_activate = reminder(:ping)&.name
109+
end
110+
103111
def ping
104112
end
105113
end
@@ -327,6 +335,20 @@ def state_of(actor_type)
327335
assert_equal "ping", snapshot.observable_values.fetch("armed")
328336
end
329337

338+
test "an actor restored after a failed turn still reads its schedule" do
339+
SolidObjects.configuration.max_attempts = 1
340+
reference = HookActor.ref("one")
341+
reference.async.arm
342+
drain
343+
# Both messages run in one worker pass, so the activation that the failure
344+
# rebuilt is the one that serves the read.
345+
reference.async.fail_turn
346+
reference.async.record_seen
347+
drain
348+
349+
assert_equal "ping", state_of("cancel-hooks").fetch("seen_on_activate")
350+
end
351+
330352
test "a cancel that lands on a claimed occurrence does not fail the scheduler" do
331353
reference = TrialActor.ref("alice")
332354
reference.async.start_recurring

0 commit comments

Comments
 (0)