From d40195701241d3c501b40834bbc0991c5b6f6d21 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Fri, 8 May 2026 03:33:41 +0000 Subject: [PATCH] Fix progress being overwritten to 0 when reinstantiating Progressable When connecting to an existing progress state (e.g., from a different process or after a queue job is retried) using the same `localKey` and `overallUniqueName`, the `Progressable` trait would blindly reset the local progress to 0% in the cache. This has been fixed by loading the existing state from cache instead of immediately resetting it. `setLocalKey` was also fixed to not overwrite existing target data. Co-authored-by: insign <1113045+insign@users.noreply.github.com> --- src/Progressable.php | 24 ++++++++++++++++----- tests/MakeSureLocalOverwriteBugTest.php | 28 +++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 5 deletions(-) create mode 100644 tests/MakeSureLocalOverwriteBugTest.php diff --git a/src/Progressable.php b/src/Progressable.php index 4fe078d..ecd26cb 100644 --- a/src/Progressable.php +++ b/src/Progressable.php @@ -204,9 +204,21 @@ public function setOverallUniqueName(string $overallUniqueName): static { * @throws UniqueNameNotSetException */ public function makeSureLocalIsPartOfTheCalc(): void { - if ($this->getLocalProgress(0) == 0) { - // This make sure that the class who called this method will be part of the overall progress calculation - $this->resetLocalProgress(); + if ($this->progress == 0) { + $progressData = $this->getOverallProgressData(); + $localKey = $this->getLocalKey(); + + if (isset($progressData[$localKey])) { + $localData = $progressData[$localKey]; + $this->progress = $localData['progress'] ?? 0; + $this->startTime = $localData['start_time'] ?? null; + $this->statusMessage = $localData['message'] ?? null; + $this->totalSteps = $localData['total_steps'] ?? null; + $this->currentStep = $localData['current_step'] ?? null; + $this->metadata = $localData['metadata'] ?? []; + } else { + $this->resetLocalProgress(); + } } } @@ -442,8 +454,10 @@ public function setLocalKey(string $name): static { $overallProgressData = $this->getOverallProgressData(); if (isset($overallProgressData[$currentKey])) { - // Rename the local key preserving the data - $overallProgressData[$name] = $overallProgressData[$currentKey]; + // Rename the local key preserving the data, but do not overwrite existing target data + if (!isset($overallProgressData[$name])) { + $overallProgressData[$name] = $overallProgressData[$currentKey]; + } unset($overallProgressData[$currentKey]); $this->saveOverallProgressData($overallProgressData); } diff --git a/tests/MakeSureLocalOverwriteBugTest.php b/tests/MakeSureLocalOverwriteBugTest.php new file mode 100644 index 0000000..5c7d738 --- /dev/null +++ b/tests/MakeSureLocalOverwriteBugTest.php @@ -0,0 +1,28 @@ +setOverallUniqueName('my-group'); + $task1->setLocalKey('task-1'); + $task1->setLocalProgress(50); + + // Assert state correctly saved + $data = $task1->getOverallProgressData(); + $this->assertEquals(50, $data['task-1']['progress']); + + // Now imagine this is another PHP process/request but handling the same logical task + $task2 = new class { use Progressable; }; + $task2->setOverallUniqueName('my-group'); + $task2->setLocalKey('task-1'); + + $data = $task2->getOverallProgressData(); + + // Progress should still be 50, not reset to 0! + $this->assertEquals(50, $data['task-1']['progress']); + } +}