Skip to content

Commit 4bfd86a

Browse files
TD-068: DefaultServiceControlPlane activity_execution and invocable_carrier_request dispatchers mark Started without invoking the handler (#532)
1 parent 057007f commit 4bfd86a

2 files changed

Lines changed: 51 additions & 8 deletions

File tree

src/V2/Support/DefaultServiceControlPlane.php

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1039,7 +1039,7 @@ private function dispatchActivityExecution(
10391039
}
10401040
}
10411041

1042-
$this->markStarted(
1042+
$this->stampBindingResolution(
10431043
$call,
10441044
ServiceCallBindingKind::ActivityExecution->value,
10451045
$activityExecutionId,
@@ -1103,7 +1103,7 @@ private function dispatchInvocableCarrierRequest(
11031103
$metadata['carrier'] = $carrierName;
11041104
}
11051105

1106-
$this->markStarted(
1106+
$this->stampBindingResolution(
11071107
$call,
11081108
ServiceCallBindingKind::InvocableCarrierRequest->value,
11091109
$carrierRequestId,
@@ -1123,6 +1123,39 @@ private function dispatchInvocableCarrierRequest(
11231123
];
11241124
}
11251125

1126+
/**
1127+
* Commit the resolved binding kind, target reference, linked workflow
1128+
* pointers, and binding-derived metadata to the durable row while leaving
1129+
* the call in Accepted.
1130+
*
1131+
* The Started transition belongs to the execution lane that actually picks
1132+
* up the activity execution or invocable carrier request; the control
1133+
* plane's contract for these binding kinds ends at admission. See
1134+
* docs/architecture/workflow-service-calls-architecture.md (Service-call
1135+
* lifecycle): Accepted is "Resolution committed, handler binding written,
1136+
* handler not yet started." Started is reserved for "Handler has begun
1137+
* executing the linked target reference."
1138+
*
1139+
* @param array<string, mixed> $metadata
1140+
*/
1141+
private function stampBindingResolution(
1142+
WorkflowServiceCall $call,
1143+
string $bindingKind,
1144+
string $targetReference,
1145+
?string $instanceId,
1146+
?string $runId,
1147+
?string $updateId,
1148+
array $metadata,
1149+
): void {
1150+
$call->resolved_binding_kind = $bindingKind;
1151+
$call->resolved_target_reference = $targetReference;
1152+
$call->linked_workflow_instance_id = $instanceId;
1153+
$call->linked_workflow_run_id = $runId;
1154+
$call->linked_workflow_update_id = $updateId;
1155+
$call->metadata = $this->mergeMetadata($call->metadata, $metadata);
1156+
$call->save();
1157+
}
1158+
11261159
/**
11271160
* @param array<string, mixed> $metadata
11281161
*/

tests/Unit/V2/DefaultServiceControlPlaneTest.php

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,7 @@ public function testExplicitPreAdmittedCallStillDispatchesWhenIdempotencyKeyIsPr
305305
$this->assertCount(1, $fakeWorkflow->starts);
306306
}
307307

308-
public function testActivityBindingMarksCallStartedWithGeneratedActivityExecutionReference(): void
308+
public function testActivityBindingCommitsBindingResolutionAtAcceptanceWithoutFalselyMarkingStarted(): void
309309
{
310310
$controlPlane = new DefaultServiceControlPlane(
311311
new FakeServiceWorkflowControlPlane(),
@@ -330,7 +330,10 @@ public function testActivityBindingMarksCallStartedWithGeneratedActivityExecutio
330330
]);
331331

332332
$this->assertTrue($result['accepted']);
333-
$this->assertSame(ServiceCallStatus::Started->value, $result['status']);
333+
$this->assertSame(ServiceCallStatus::Accepted->value, $result['status']);
334+
$this->assertSame(ServiceCallOutcome::Accepted->value, $result['outcome']);
335+
$this->assertNotNull($result['accepted_at']);
336+
$this->assertNull($result['started_at']);
334337
$this->assertSame(ServiceCallBindingKind::ActivityExecution->value, $result['resolved_binding_kind']);
335338
$this->assertNotNull($result['resolved_target_reference']);
336339
$this->assertSame($result['resolved_target_reference'], $result['handler']['activity_execution_id']);
@@ -340,8 +343,10 @@ public function testActivityBindingMarksCallStartedWithGeneratedActivityExecutio
340343

341344
$call = WorkflowServiceCall::query()->firstOrFail();
342345

343-
$this->assertSame(ServiceCallStatus::Started->value, $call->status);
346+
$this->assertSame(ServiceCallStatus::Accepted->value, $call->status);
344347
$this->assertSame(ServiceCallOutcome::Accepted, $call->outcome);
348+
$this->assertNotNull($call->accepted_at);
349+
$this->assertNull($call->started_at);
345350
$this->assertSame($result['resolved_target_reference'], $call->resolved_target_reference);
346351
$this->assertSame($result['resolved_target_reference'], $call->metadata['activity_execution_id']);
347352
$this->assertSame('App\\Activities\\IssueInvoice', $call->metadata['activity_class']);
@@ -373,7 +378,7 @@ public function testActivityBindingFailsWhenActivityClassAndReferenceMissing():
373378
$this->assertSame(ServiceCallOutcome::HandlerFailed->value, $result['outcome']);
374379
}
375380

376-
public function testInvocableCarrierBindingMarksCallStartedWithGeneratedCarrierRequestReference(): void
381+
public function testInvocableCarrierBindingCommitsBindingResolutionAtAcceptanceWithoutFalselyMarkingStarted(): void
377382
{
378383
$controlPlane = new DefaultServiceControlPlane(
379384
new FakeServiceWorkflowControlPlane(),
@@ -398,7 +403,10 @@ public function testInvocableCarrierBindingMarksCallStartedWithGeneratedCarrierR
398403
]);
399404

400405
$this->assertTrue($result['accepted']);
401-
$this->assertSame(ServiceCallStatus::Started->value, $result['status']);
406+
$this->assertSame(ServiceCallStatus::Accepted->value, $result['status']);
407+
$this->assertSame(ServiceCallOutcome::Accepted->value, $result['outcome']);
408+
$this->assertNotNull($result['accepted_at']);
409+
$this->assertNull($result['started_at']);
402410
$this->assertSame(ServiceCallBindingKind::InvocableCarrierRequest->value, $result['resolved_binding_kind']);
403411
$this->assertNotNull($result['resolved_target_reference']);
404412
$this->assertSame($result['resolved_target_reference'], $result['handler']['carrier_request_id']);
@@ -409,8 +417,10 @@ public function testInvocableCarrierBindingMarksCallStartedWithGeneratedCarrierR
409417

410418
$call = WorkflowServiceCall::query()->firstOrFail();
411419

412-
$this->assertSame(ServiceCallStatus::Started->value, $call->status);
420+
$this->assertSame(ServiceCallStatus::Accepted->value, $call->status);
413421
$this->assertSame(ServiceCallOutcome::Accepted, $call->outcome);
422+
$this->assertNotNull($call->accepted_at);
423+
$this->assertNull($call->started_at);
414424
$this->assertSame($result['resolved_target_reference'], $call->metadata['carrier_request_id']);
415425
$this->assertSame('https://carrier.billing.example/handle', $call->metadata['carrier_endpoint']);
416426
$this->assertSame('billing.invoice.issue', $call->metadata['carrier_handler']);

0 commit comments

Comments
 (0)