Skip to content

Speed up Psalm analysis of activity and workflow jobs#402

Merged
rmcdaniel merged 1 commit into
masterfrom
agent/fix-psalm-activity-analysis
Jul 19, 2026
Merged

Speed up Psalm analysis of activity and workflow jobs#402
rmcdaniel merged 1 commit into
masterfrom
agent/fix-psalm-activity-analysis

Conversation

@rmcdaniel

@rmcdaniel rmcdaniel commented Jul 19, 2026

Copy link
Copy Markdown
Member

Fixes #399.

Root cause

Activity and Workflow initialized Laravel queue routing through Queueable's fluent setters on the subclass-typed $this. Psalm specializes the trait's $this return type for every job subclass, then repeatedly reports that Queueable.php cannot use its cache.

The refactor in #401 does not touch that call path; the compact reproduction below remains at 19 total / 17 analysis-phase Queueable reparses on both master and #401.

Fix

  • pass each job through private static helpers with a concrete Activity or Workflow receiver
  • keep the original effectiveConnection → onConnection → effectiveQueue → onQueue order
  • preserve overridable onConnection() / onQueue() dispatch, subclass defaults, and Laravel's enum normalization
  • cover effective values, mixed null values, subclass defaults, and setter dispatch

Results

Pinned environment: PHP 8.5.8, Laravel 13.20.0, Psalm 6.16.1. All runs completed with no Psalm errors.

Compact reproduction Total Queueable reparses Analysis-phase reparses
master (d502e26) 19 17
PR #401 (8bf64429) 19 17
this PR 3 1

A 120-activity amplification microbenchmark using one PSR-4 file per activity completed in 10.38s on master and 7.37s on this PR. Reparse counts fell from 963 to 3. With Psalm's cache warmed, they remained 962 on master and fell to 2 here.

Validation

  • composer ecs
  • composer stan
  • focused Activity and Workflow routing tests
  • Laravel 13 runtime probe for overridden setters, lookup order, and enum-backed defaults
  • Psalm compact reproduction and 120-activity amplification microbenchmark
  • GitHub Actions build and Codecov patch/project checks
Copy/paste Psalm reproduction
repro_dir="$(mktemp -d)"
trap 'rm -rf "$repro_dir"' EXIT

git clone --quiet https://github.com/durable-workflow/workflow.git "$repro_dir/repo"
git -C "$repro_dir/repo" worktree add --detach "$repro_dir/master" d502e26e91818afcc96164e9962a38d0da453909
git -C "$repro_dir/repo" worktree add --detach "$repro_dir/pr" 02af9b1c6c8bf6571e9cc5187107fde0097b5246
mkdir -p "$repro_dir/bench/src"

cat >"$repro_dir/bench/composer.json" <<'JSON'
{
  "name": "durable-workflow/issue-399-repro",
  "type": "project",
  "require": {
    "php": "^8.3",
    "durable-workflow/workflow": "1.0.78",
    "laravel/framework": "13.20.0",
    "vimeo/psalm": "6.16.1"
  },
  "repositories": [{
    "type": "path",
    "url": "/workflow",
    "options": {
      "symlink": true,
      "versions": {"durable-workflow/workflow": "1.0.78"}
    }
  }]
}
JSON

cat >"$repro_dir/bench/psalm.xml" <<'XML'
<?xml version="1.0"?>
<psalm xmlns="https://getpsalm.org/schema/config"
       cacheDirectory=".psalm-cache"
       errorLevel="1"
       findUnusedCode="false">
  <projectFiles>
    <directory name="src" />
    <ignoreFiles>
      <directory name="vendor" />
      <directory name="/workflow" />
    </ignoreFiles>
  </projectFiles>
</psalm>
XML

cat >"$repro_dir/bench/src/Repro.php" <<'PHP'
<?php

declare(strict_types=1);

namespace Repro;

use Generator;
use Workflow\Activity;
use Workflow\Workflow;

final class SlotPublishWorkflow extends Workflow
{
    public function execute(): Generator
    {
        yield from [];
    }
}

final class PublishSlotActivity extends Activity
{
    public function execute(SlotPublisher $publisher, Slot $slot): void
    {
        $publisher->publish($slot);
    }
}

final class SlotPublisher
{
    public function publish(Slot $slot): void
    {
    }
}

final class Slot
{
}
PHP

docker run --rm -u "$(id -u):$(id -g)" \
  -e COMPOSER_HOME=/tmp/composer \
  -v "$repro_dir/master:/workflow:ro" \
  -v "$repro_dir/bench:/bench" -w /bench \
  composer:2.10.2 composer update --no-interaction --prefer-dist --no-progress

run_psalm () {
  repro_label="$1"
  workflow_source="$2"

  docker run --rm -u "$(id -u):$(id -g)" \
    -v "$workflow_source:/workflow:ro" \
    -v "$repro_dir/bench:/bench" -w /bench \
    composer:2.10.2 \
    vendor/bin/psalm --debug --no-cache --threads=1 src/Repro.php \
    >"$repro_dir/bench/$repro_label.log" 2>&1

  queueable_total="$(grep -c 'Queueable.php because we cannot use cache' "$repro_dir/bench/$repro_label.log")"
  queueable_analysis="$(awk '/Analyzing files/{seen=1; next} seen && /Queueable.php because we cannot use cache/{count++} END{print count+0}' "$repro_dir/bench/$repro_label.log")"
  printf '%s: total=%s analysis=%s\n' "$repro_label" "$queueable_total" "$queueable_analysis"
}

run_psalm master "$repro_dir/master"
run_psalm pr-402 "$repro_dir/pr"

Expected output:

master: total=19 analysis=17
pr-402: total=3 analysis=1

Both Psalm logs end with No errors found!.

@codecov

codecov Bot commented Jul 19, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (d502e26) to head (02af9b1).

Additional details and impacted files
@@             Coverage Diff             @@
##              master      #402   +/-   ##
===========================================
  Coverage     100.00%   100.00%           
- Complexity       666       670    +4     
===========================================
  Files             63        63           
  Lines           2267      2275    +8     
===========================================
+ Hits            2267      2275    +8     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@rmcdaniel
rmcdaniel force-pushed the agent/fix-psalm-activity-analysis branch from 871126d to 02af9b1 Compare July 19, 2026 18:29
@rmcdaniel
rmcdaniel marked this pull request as ready for review July 19, 2026 18:43
@rmcdaniel
rmcdaniel merged commit 4959d4c into master Jul 19, 2026
3 checks passed
@rmcdaniel
rmcdaniel deleted the agent/fix-psalm-activity-analysis branch July 19, 2026 18:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants