From 170194e9759a56134fe416a467ae0cf1d687dd61 Mon Sep 17 00:00:00 2001 From: Richard McDaniel Date: Thu, 23 Mar 2023 15:28:34 +0000 Subject: [PATCH 01/10] Restart child workflows --- src/ChildWorkflowStub.php | 26 ++++++++++++++++++++++++-- src/Models/StoredWorkflow.php | 10 ++++++++++ src/WorkflowStub.php | 15 +++++++++++++-- 3 files changed, 47 insertions(+), 4 deletions(-) diff --git a/src/ChildWorkflowStub.php b/src/ChildWorkflowStub.php index e15fe50..be77133 100644 --- a/src/ChildWorkflowStub.php +++ b/src/ChildWorkflowStub.php @@ -31,8 +31,30 @@ public static function make($workflow, ...$arguments): PromiseInterface return resolve(Y::unserialize($log->result)); } - WorkflowStub::make($workflow) - ->startAsChild($context->storedWorkflow, $context->index, $context->now, ...$arguments); + $childWorkflowRunning = false; + + $storedChildWorkflow = $context->storedWorkflow->children() + ->wherePivot('parent_index', $context->index) + ->first(); + + if ($storedChildWorkflow) { + $childWorkflow = $storedChildWorkflow->toWorkflow(); + if ($childWorkflow->running()) { + try { + $childWorkflow->resume(); + } catch (\Spatie\ModelStates\Exceptions\TransitionNotFound) { + } + $childWorkflowRunning = true; + } elseif ($childWorkflow->status() === WorkflowFailedStatus::class) { + $childWorkflow->restartAsChild($context->storedWorkflow, $context->index, $context->now, ...$arguments); + $childWorkflowRunning = true; + } + } + + if (! $childWorkflowRunning) { + WorkflowStub::make($workflow) + ->startAsChild($context->storedWorkflow, $context->index, $context->now, ...$arguments); + } ++$context->index; WorkflowStub::setContext($context); diff --git a/src/Models/StoredWorkflow.php b/src/Models/StoredWorkflow.php index 6645174..5a042a0 100644 --- a/src/Models/StoredWorkflow.php +++ b/src/Models/StoredWorkflow.php @@ -66,4 +66,14 @@ public function parents(): \Illuminate\Database\Eloquent\Relations\BelongsToMany 'parent_workflow_id' )->withPivot(['parent_index', 'parent_now']); } + + public function children(): \Illuminate\Database\Eloquent\Relations\BelongsToMany + { + return $this->belongsToMany( + config('workflows.stored_workflow_model', self::class), + 'workflow_relationships', + 'parent_workflow_id', + 'child_workflow_id' + )->withPivot(['parent_index', 'parent_now']); + } } diff --git a/src/WorkflowStub.php b/src/WorkflowStub.php index 196022c..0563600 100644 --- a/src/WorkflowStub.php +++ b/src/WorkflowStub.php @@ -158,6 +158,8 @@ public function restart(...$arguments): void ->delete(); $this->storedWorkflow->timers() ->delete(); + $this->storedWorkflow->parents() + ->detach(); $this->dispatch(); } @@ -176,15 +178,24 @@ public function start(...$arguments): void public function startAsChild(StoredWorkflow $parentWorkflow, int $index, $now, ...$arguments): void { - $this->storedWorkflow->arguments = Y::serialize($arguments); + $this->storedWorkflow->parents() + ->attach($parentWorkflow, [ + 'parent_index' => $index, + 'parent_now' => $now, + ]); + + $this->start($arguments); + } + public function restartAsChild(StoredWorkflow $parentWorkflow, int $index, $now, ...$arguments): void + { $this->storedWorkflow->parents() ->attach($parentWorkflow, [ 'parent_index' => $index, 'parent_now' => $now, ]); - $this->dispatch(); + $this->restart($arguments); } public function fail($exception): void From 7c9b5bfc85cca5a000ec62c4ddf7d21721b30b96 Mon Sep 17 00:00:00 2001 From: Richard McDaniel Date: Thu, 23 Mar 2023 15:35:30 +0000 Subject: [PATCH 02/10] PHPStan --- src/ChildWorkflowStub.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ChildWorkflowStub.php b/src/ChildWorkflowStub.php index be77133..d8327ed 100644 --- a/src/ChildWorkflowStub.php +++ b/src/ChildWorkflowStub.php @@ -9,6 +9,7 @@ use React\Promise\PromiseInterface; use function React\Promise\resolve; use Workflow\Serializers\Y; +use Workflow\States\WorkflowFailedStatus; final class ChildWorkflowStub { From 0a61ae6e54baca6523f378a18e14c8afdb8c0fd5 Mon Sep 17 00:00:00 2001 From: Richard McDaniel Date: Thu, 23 Mar 2023 19:01:12 +0000 Subject: [PATCH 03/10] Add tests --- src/ChildWorkflowStub.php | 5 +- src/WorkflowStub.php | 46 ++++++++----------- tests/Feature/ParentWorkflowTest.php | 28 +++++++++++ tests/Fixtures/TestChildExceptionWorkflow.php | 27 +++++++++++ .../Fixtures/TestParentExceptionWorkflow.php | 25 ++++++++++ tests/Unit/WorkflowStubTest.php | 5 -- 6 files changed, 100 insertions(+), 36 deletions(-) create mode 100644 tests/Fixtures/TestChildExceptionWorkflow.php create mode 100644 tests/Fixtures/TestParentExceptionWorkflow.php diff --git a/src/ChildWorkflowStub.php b/src/ChildWorkflowStub.php index d8327ed..f972ba1 100644 --- a/src/ChildWorkflowStub.php +++ b/src/ChildWorkflowStub.php @@ -9,7 +9,6 @@ use React\Promise\PromiseInterface; use function React\Promise\resolve; use Workflow\Serializers\Y; -use Workflow\States\WorkflowFailedStatus; final class ChildWorkflowStub { @@ -46,8 +45,8 @@ public static function make($workflow, ...$arguments): PromiseInterface } catch (\Spatie\ModelStates\Exceptions\TransitionNotFound) { } $childWorkflowRunning = true; - } elseif ($childWorkflow->status() === WorkflowFailedStatus::class) { - $childWorkflow->restartAsChild($context->storedWorkflow, $context->index, $context->now, ...$arguments); + } elseif ($childWorkflow->failed()) { + $childWorkflow->startAsChild($context->storedWorkflow, $context->index, $context->now, ...$arguments); $childWorkflowRunning = true; } } diff --git a/src/WorkflowStub.php b/src/WorkflowStub.php index 0563600..6a6e742 100644 --- a/src/WorkflowStub.php +++ b/src/WorkflowStub.php @@ -133,6 +133,11 @@ public function running(): bool return ! in_array($this->status(), [WorkflowCompletedStatus::class, WorkflowFailedStatus::class], true); } + public function failed(): bool + { + return $this->status() === WorkflowFailedStatus::class; + } + public function status(): string|bool { return $this->storedWorkflow->fresh() @@ -146,24 +151,6 @@ public function fresh(): static return $this; } - public function restart(...$arguments): void - { - $this->storedWorkflow->arguments = Y::serialize($arguments); - $this->storedWorkflow->output = null; - $this->storedWorkflow->exceptions() - ->delete(); - $this->storedWorkflow->logs() - ->delete(); - $this->storedWorkflow->signals() - ->delete(); - $this->storedWorkflow->timers() - ->delete(); - $this->storedWorkflow->parents() - ->detach(); - - $this->dispatch(); - } - public function resume(): void { $this->dispatch(); @@ -179,27 +166,20 @@ public function start(...$arguments): void public function startAsChild(StoredWorkflow $parentWorkflow, int $index, $now, ...$arguments): void { $this->storedWorkflow->parents() - ->attach($parentWorkflow, [ - 'parent_index' => $index, - 'parent_now' => $now, - ]); - - $this->start($arguments); - } + ->detach(); - public function restartAsChild(StoredWorkflow $parentWorkflow, int $index, $now, ...$arguments): void - { $this->storedWorkflow->parents() ->attach($parentWorkflow, [ 'parent_index' => $index, 'parent_now' => $now, ]); - $this->restart($arguments); + $this->start(...$arguments); } public function fail($exception): void { + // file_put_contents('/workspace/log.txt', file_get_contents('/workspace/log.txt') . 'ok1' . PHP_EOL); try { $this->storedWorkflow->exceptions() ->create([ @@ -211,6 +191,16 @@ public function fail($exception): void } $this->storedWorkflow->status->transitionTo(WorkflowFailedStatus::class); + + $this->storedWorkflow->parents() + ->each(static function ($parentWorkflow) use ($exception) { + try { + $parentWorkflow->toWorkflow() + ->fail($exception); + } catch (\Spatie\ModelStates\Exceptions\TransitionNotFound) { + return; + } + }); } public function next($index, $now, $class, $result): void diff --git a/tests/Feature/ParentWorkflowTest.php b/tests/Feature/ParentWorkflowTest.php index f774ebd..02b0913 100644 --- a/tests/Feature/ParentWorkflowTest.php +++ b/tests/Feature/ParentWorkflowTest.php @@ -5,10 +5,13 @@ namespace Tests\Feature; use Tests\Fixtures\TestActivity; +use Tests\Fixtures\TestChildExceptionWorkflow; use Tests\Fixtures\TestChildWorkflow; +use Tests\Fixtures\TestParentExceptionWorkflow; use Tests\Fixtures\TestParentWorkflow; use Tests\TestCase; use Workflow\States\WorkflowCompletedStatus; +use Workflow\States\WorkflowFailedStatus; use Workflow\WorkflowStub; final class ParentWorkflowTest extends TestCase @@ -29,4 +32,29 @@ public function testCompleted(): void ->values() ->toArray()); } + + public function testRetry(): void + { + $workflow = WorkflowStub::make(TestParentExceptionWorkflow::class); + + $workflow->start(shouldThrow: true); + + while ($workflow->running()); + + $this->assertSame(WorkflowFailedStatus::class, $workflow->status()); + $this->assertNull($workflow->output()); + + $workflow->fresh() + ->start(shouldThrow: false); + + while ($workflow->running()); + + $this->assertSame(WorkflowCompletedStatus::class, $workflow->status()); + $this->assertSame('workflow_activity_other', $workflow->output()); + $this->assertSame([TestActivity::class, TestChildExceptionWorkflow::class], $workflow->logs() + ->pluck('class') + ->sort() + ->values() + ->toArray()); + } } diff --git a/tests/Fixtures/TestChildExceptionWorkflow.php b/tests/Fixtures/TestChildExceptionWorkflow.php new file mode 100644 index 0000000..c156d4c --- /dev/null +++ b/tests/Fixtures/TestChildExceptionWorkflow.php @@ -0,0 +1,27 @@ +fail(new Exception('test')); $this->assertSame(WorkflowFailedStatus::class, $workflow->status()); - $workflow->restart(); - $workflow->fresh(); - $this->assertSame(WorkflowPendingStatus::class, $workflow->status()); - $this->assertSame(0, $workflow->logs()->count()); - $workflow->cancel(); while (! $workflow->isCanceled()); From f260c01e5b7fa7f7ad54de8324a40604e2959842 Mon Sep 17 00:00:00 2001 From: Richard McDaniel Date: Thu, 23 Mar 2023 19:07:23 +0000 Subject: [PATCH 04/10] Fix test --- tests/Unit/WorkflowStubTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Unit/WorkflowStubTest.php b/tests/Unit/WorkflowStubTest.php index 703db92..72df404 100644 --- a/tests/Unit/WorkflowStubTest.php +++ b/tests/Unit/WorkflowStubTest.php @@ -42,7 +42,7 @@ public function testMake(): void $workflow->fresh(); $this->assertSame(WorkflowPendingStatus::class, $workflow->status()); $this->assertNull($workflow->output()); - $this->assertSame(1, $workflow->logs()->count()); + $this->assertSame(2, $workflow->logs()->count()); } public function testComplete(): void From 79c7591e1f67d23bfd88fc4f478194fbe66230f2 Mon Sep 17 00:00:00 2001 From: Richard McDaniel Date: Thu, 23 Mar 2023 20:06:58 +0000 Subject: [PATCH 05/10] Refactor --- src/ChildWorkflowStub.php | 26 ++++++++------------------ src/WorkflowStub.php | 15 +++++++++++++-- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/src/ChildWorkflowStub.php b/src/ChildWorkflowStub.php index f972ba1..ad48779 100644 --- a/src/ChildWorkflowStub.php +++ b/src/ChildWorkflowStub.php @@ -31,29 +31,19 @@ public static function make($workflow, ...$arguments): PromiseInterface return resolve(Y::unserialize($log->result)); } - $childWorkflowRunning = false; - $storedChildWorkflow = $context->storedWorkflow->children() ->wherePivot('parent_index', $context->index) ->first(); - if ($storedChildWorkflow) { - $childWorkflow = $storedChildWorkflow->toWorkflow(); - if ($childWorkflow->running()) { - try { - $childWorkflow->resume(); - } catch (\Spatie\ModelStates\Exceptions\TransitionNotFound) { - } - $childWorkflowRunning = true; - } elseif ($childWorkflow->failed()) { - $childWorkflow->startAsChild($context->storedWorkflow, $context->index, $context->now, ...$arguments); - $childWorkflowRunning = true; - } - } + $childWorkflow = $storedChildWorkflow ? $storedChildWorkflow->toWorkflow() : WorkflowStub::make($workflow); - if (! $childWorkflowRunning) { - WorkflowStub::make($workflow) - ->startAsChild($context->storedWorkflow, $context->index, $context->now, ...$arguments); + if ($childWorkflow->running() && ! $childWorkflow->created()) { + try { + $childWorkflow->resume(); + } catch (\Spatie\ModelStates\Exceptions\TransitionNotFound) { + } + } elseif (! $childWorkflow->completed()) { + $childWorkflow->startAsChild($context->storedWorkflow, $context->index, $context->now, ...$arguments); } ++$context->index; diff --git a/src/WorkflowStub.php b/src/WorkflowStub.php index 6a6e742..955a328 100644 --- a/src/WorkflowStub.php +++ b/src/WorkflowStub.php @@ -10,6 +10,7 @@ use Workflow\Models\StoredWorkflow; use Workflow\Serializers\Y; use Workflow\States\WorkflowCompletedStatus; +use Workflow\States\WorkflowCreatedStatus; use Workflow\States\WorkflowFailedStatus; use Workflow\States\WorkflowPendingStatus; use Workflow\Traits\Awaits; @@ -128,9 +129,14 @@ public function output() return Y::unserialize($this->storedWorkflow->fresh()->output); } - public function running(): bool + public function completed(): bool { - return ! in_array($this->status(), [WorkflowCompletedStatus::class, WorkflowFailedStatus::class], true); + return $this->status() === WorkflowCompletedStatus::class; + } + + public function created(): bool + { + return $this->status() === WorkflowCreatedStatus::class; } public function failed(): bool @@ -138,6 +144,11 @@ public function failed(): bool return $this->status() === WorkflowFailedStatus::class; } + public function running(): bool + { + return ! in_array($this->status(), [WorkflowCompletedStatus::class, WorkflowFailedStatus::class], true); + } + public function status(): string|bool { return $this->storedWorkflow->fresh() From a5b0542fe92bb0e1f33b6e6886d8a9ead5f7444f Mon Sep 17 00:00:00 2001 From: Richard McDaniel Date: Thu, 23 Mar 2023 20:43:00 +0000 Subject: [PATCH 06/10] Add comment --- src/ChildWorkflowStub.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ChildWorkflowStub.php b/src/ChildWorkflowStub.php index ad48779..530e5d9 100644 --- a/src/ChildWorkflowStub.php +++ b/src/ChildWorkflowStub.php @@ -41,6 +41,7 @@ public static function make($workflow, ...$arguments): PromiseInterface try { $childWorkflow->resume(); } catch (\Spatie\ModelStates\Exceptions\TransitionNotFound) { + // already running } } elseif (! $childWorkflow->completed()) { $childWorkflow->startAsChild($context->storedWorkflow, $context->index, $context->now, ...$arguments); From 974a76560934512771dbf875ff3a33da670fc19d Mon Sep 17 00:00:00 2001 From: Richard McDaniel Date: Thu, 23 Mar 2023 21:40:26 +0000 Subject: [PATCH 07/10] Add test --- tests/Unit/ChildWorkflowStubTest.php | 30 ++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/tests/Unit/ChildWorkflowStubTest.php b/tests/Unit/ChildWorkflowStubTest.php index 0a25499..3f1037f 100644 --- a/tests/Unit/ChildWorkflowStubTest.php +++ b/tests/Unit/ChildWorkflowStubTest.php @@ -58,6 +58,36 @@ public function testLoadsStoredResult(): void $this->assertSame('test', $result); } + public function testLoadsChildWorkflow(): void + { + $workflow = WorkflowStub::load(WorkflowStub::make(TestParentWorkflow::class)->id()); + $storedWorkflow = StoredWorkflow::findOrFail($workflow->id()); + $storedWorkflow->update([ + 'arguments' => Y::serialize([]), + 'status' => WorkflowPendingStatus::$name, + ]); + + $childWorkflow = WorkflowStub::load(WorkflowStub::make(TestChildWorkflow::class)->id()); + $storedChildWorkflow = StoredWorkflow::findOrFail($childWorkflow->id()); + $storedChildWorkflow->update([ + 'arguments' => Y::serialize([]), + 'status' => WorkflowPendingStatus::$name, + ]); + $storedChildWorkflow->parents()->attach($storedWorkflow, [ + 'parent_index' => 0, + 'parent_now' => now(), + ]); + + $workflow = $storedWorkflow->toWorkflow(); + + $existingChildWorkflow = ChildWorkflowStub::make(TestChildWorkflow::class) + ->then(static function ($value) use (&$result) { + $result = $value; + }); + + $this->assertNull($result); + } + public function testAll(): void { $workflow = WorkflowStub::load(WorkflowStub::make(TestParentWorkflow::class)->id()); From 240bee2c9f82a3cd48da371a66845f9a0b539d90 Mon Sep 17 00:00:00 2001 From: Richard McDaniel Date: Thu, 23 Mar 2023 21:42:20 +0000 Subject: [PATCH 08/10] ECS --- tests/Unit/ChildWorkflowStubTest.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/Unit/ChildWorkflowStubTest.php b/tests/Unit/ChildWorkflowStubTest.php index 3f1037f..5fe781b 100644 --- a/tests/Unit/ChildWorkflowStubTest.php +++ b/tests/Unit/ChildWorkflowStubTest.php @@ -73,10 +73,11 @@ public function testLoadsChildWorkflow(): void 'arguments' => Y::serialize([]), 'status' => WorkflowPendingStatus::$name, ]); - $storedChildWorkflow->parents()->attach($storedWorkflow, [ - 'parent_index' => 0, - 'parent_now' => now(), - ]); + $storedChildWorkflow->parents() + ->attach($storedWorkflow, [ + 'parent_index' => 0, + 'parent_now' => now(), + ]); $workflow = $storedWorkflow->toWorkflow(); From c66e6b71a4b2e5d5f866d34123ae95dc17ec1134 Mon Sep 17 00:00:00 2001 From: Richard McDaniel Date: Thu, 23 Mar 2023 22:08:44 +0000 Subject: [PATCH 09/10] Refactor --- src/WorkflowStub.php | 1 - tests/Unit/WorkflowStubTest.php | 16 +++++++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/WorkflowStub.php b/src/WorkflowStub.php index 955a328..31e4a67 100644 --- a/src/WorkflowStub.php +++ b/src/WorkflowStub.php @@ -190,7 +190,6 @@ public function startAsChild(StoredWorkflow $parentWorkflow, int $index, $now, . public function fail($exception): void { - // file_put_contents('/workspace/log.txt', file_get_contents('/workspace/log.txt') . 'ok1' . PHP_EOL); try { $this->storedWorkflow->exceptions() ->create([ diff --git a/tests/Unit/WorkflowStubTest.php b/tests/Unit/WorkflowStubTest.php index 72df404..13ab828 100644 --- a/tests/Unit/WorkflowStubTest.php +++ b/tests/Unit/WorkflowStubTest.php @@ -9,6 +9,7 @@ use Tests\Fixtures\TestAwaitWorkflow; use Tests\Fixtures\TestWorkflow; use Tests\TestCase; +use Workflow\Models\StoredWorkflow; use Workflow\Serializers\Y; use Workflow\Signal; use Workflow\States\WorkflowCompletedStatus; @@ -22,7 +23,15 @@ public function testMake(): void { Carbon::setTestNow('2022-01-01'); + $parentWorkflow = WorkflowStub::load(WorkflowStub::make(TestWorkflow::class)->id()); + $storedParentWorkflow = StoredWorkflow::findOrFail($parentWorkflow->id()); + $storedParentWorkflow->update([ + 'arguments' => Y::serialize([]), + 'status' => WorkflowPendingStatus::$name, + ]); + $workflow = WorkflowStub::load(WorkflowStub::make(TestWorkflow::class)->id()); + $storedWorkflow = StoredWorkflow::findOrFail($workflow->id()); $workflow->start(); $workflow->cancel(); while (! $workflow->isCanceled()); @@ -33,8 +42,13 @@ public function testMake(): void $this->assertNull($workflow->output()); $this->assertSame(1, $workflow->logs()->count()); + $storedWorkflow->parents()->attach($storedParentWorkflow, [ + 'parent_index' => 0, + 'parent_now' => now(), + ]); $workflow->fail(new Exception('test')); - $this->assertSame(WorkflowFailedStatus::class, $workflow->status()); + $this->assertTrue($workflow->failed()); + $this->assertTrue($parentWorkflow->failed()); $workflow->cancel(); while (! $workflow->isCanceled()); From 14aaccc85de47ddb44c4688111dcfc057ade52a0 Mon Sep 17 00:00:00 2001 From: Richard McDaniel Date: Thu, 23 Mar 2023 22:13:36 +0000 Subject: [PATCH 10/10] ECS --- tests/Unit/WorkflowStubTest.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/Unit/WorkflowStubTest.php b/tests/Unit/WorkflowStubTest.php index 13ab828..07d6ea5 100644 --- a/tests/Unit/WorkflowStubTest.php +++ b/tests/Unit/WorkflowStubTest.php @@ -13,7 +13,6 @@ use Workflow\Serializers\Y; use Workflow\Signal; use Workflow\States\WorkflowCompletedStatus; -use Workflow\States\WorkflowFailedStatus; use Workflow\States\WorkflowPendingStatus; use Workflow\WorkflowStub; @@ -42,10 +41,11 @@ public function testMake(): void $this->assertNull($workflow->output()); $this->assertSame(1, $workflow->logs()->count()); - $storedWorkflow->parents()->attach($storedParentWorkflow, [ - 'parent_index' => 0, - 'parent_now' => now(), - ]); + $storedWorkflow->parents() + ->attach($storedParentWorkflow, [ + 'parent_index' => 0, + 'parent_now' => now(), + ]); $workflow->fail(new Exception('test')); $this->assertTrue($workflow->failed()); $this->assertTrue($parentWorkflow->failed()); pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy