Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions .github/workflows/sdk-activity-upgrades.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: published SDK and application activity upgrades

on:
push:
branches: [main]
pull_request:
branches: [main]
workflow_dispatch:

permissions:
contents: read

concurrency:
group: sdk-activity-upgrades-${{ github.event_name }}-${{ github.event_name == 'pull_request' && github.ref || github.sha }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
upgrades:
name: Pending activity SDK/application upgrades (nine directions)
runs-on: ubuntu-latest
timeout-minutes: 25
env:
SDK_ACTIVITY_UPGRADES_COMPOSE_PROJECT_NAME: sample-app-sdk-activity-upgrades-${{ github.run_id }}-${{ github.run_attempt }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Resolve the exact published tuple
run: scripts/resolve-current-artifacts.sh >> "$GITHUB_ENV"
- name: Test original-activity and package evidence
run: python3 -m unittest discover -s polyglot/python_worker/tests -p test_sdk_activity_upgrades.py -v
- name: Replay pending activities across SDK and application replacements
run: scripts/sdk-activity-upgrades.sh
44 changes: 44 additions & 0 deletions polyglot/activities/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,49 @@
# Published remote activity retry and recovery

## Pending activities across SDK and application upgrades

Run from the prepared development container with the published tuple resolved:

```bash
while IFS= read -r assignment; do export "$assignment"; done \
< <(scripts/resolve-current-artifacts.sh)
SDK_ACTIVITY_UPGRADES_COMPOSE_PROJECT_NAME=sample-app-activity-upgrades \
scripts/sdk-activity-upgrades.sh
```

This separate experiment runs eighteen originals: nine pending workflow/activity
directions across PHP, Python and Rust, then nine fresh current-package controls.
It uses the same explicit previous-package baseline as the timer upgrades and
the current qualified tuple. Both image generations are built and their actual
installed package identities checked before the first workflow starts.

Previous-package workers register the workflow without an activity handler.
Each original must reach public waiting status with one unstarted activity,
version 1/range 1..1, command sequence 2, and its complete retry/timeout policy.
The runner records all nine original histories, kills those workflow workers,
retires only their exact registrations and starts cold current-package workers
with revision-two application helpers and activity handlers.

Replayed originals must retain version 1 and every event in their original
history prefix. Fresh current runs select version 2/range 1..2 and the revised
policy. Each actual activity executes once on the current SDK after replacement,
returns exact int64/null/boolean/fraction/Unicode/list values and completes the
same original workflow once. The observer checks both durable results, actual
attempt/execution identities and completion inside the original total budget.
Python's registered wrapper and recorded definition stay identical; the selected
business helper changes. Incompatible Python class adoption and build routing
remain separate gates.

Pending admission has a twenty-second observer bound and each result a
120-second observer bound. Authored activities retain their thirty-second
attempt budget and original 120-second total budget; fresh revision-two controls
use 180 seconds total. No clock, database row or task budget is edited. Built
consumer images, project containers, volumes and networks are removed on exit. The focused
Action and logs report the actual package pairs, original IDs and outcomes;
constructed verifier tests alone do not qualify published-package replay.

## Retry, deadlines and worker loss

This experiment runs every PHP, Python and Rust workflow/activity direction.
Each workflow language calls an activity in each of the three languages,
including its own language, for nine directions.
Expand Down
7 changes: 7 additions & 0 deletions polyglot/activities/compose-sdk-upgrades.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
services:
php-same-workflow-worker:
command: ["php", "/app/activity_upgrades.php"]
python-workflow-worker:
command: ["python", "/app/activity_upgrades.py"]
rust-workflow-worker:
entrypoint: ["activity-upgrade-worker"]
1 change: 1 addition & 0 deletions polyglot/php_worker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ RUN test -n "$DURABLE_WORKFLOW_PHP_SDK_VERSION" \
COPY worker.php ./
COPY child_workflows.php ./
COPY timer_upgrades.php ./
COPY activity_upgrades.php ./
COPY php_created_rust_schedule.php ./
COPY update_client.php ./
COPY cancellation_client.php ./
Expand Down
41 changes: 41 additions & 0 deletions polyglot/php_worker/activity_upgrades.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

declare(strict_types=1);

require_once __DIR__.'/vendor/autoload.php';

use Composer\InstalledVersions;
use DurableWorkflow\Client;
use DurableWorkflow\Worker;
use DurableWorkflow\Worker\ActivityContext;
use DurableWorkflow\Worker\WorkflowContext;

$revision = getenv('POLYGLOT_TIMER_APPLICATION');
if (! in_array($revision, ['v1', 'v2'], true)) {
throw new InvalidArgumentException('Select activity application v1 or v2.');
}
$client = new Client((string) getenv('DURABLE_WORKFLOW_SERVER_URL'),
token: (string) getenv('DURABLE_WORKFLOW_AUTH_TOKEN'), namespace: 'default');
$worker = new Worker($client, 'polyglot-php', 'activity-upgrade-php-'.getenv('HOSTNAME'));
$worker->registerWorkflow('polyglot.php.activity-upgrade',
static function (WorkflowContext $context, array $request) use ($revision): array {
$version = $context->getVersion('sample-activity-application', 1, $revision === 'v1' ? 1 : 2);
$result = $context->activity('polyglot.'.$request['activity_runtime'].'.upgrade-activity',
[['case_id' => $request['case_id'], 'version' => $version, 'value' => $request['value']]],
['queue' => 'polyglot-'.$request['activity_runtime'],
'retry_policy' => ['max_attempts' => $version + 1, 'backoff_seconds' => $version === 1 ? [1] : [1, 2]],
'start_to_close_timeout' => 30, 'schedule_to_close_timeout' => $version === 1 ? 120 : 180]);

return ['workflow_runtime' => 'php', 'application_revision' => $revision,
'version' => $version, 'activity' => $result];
});
// Previous workers deliberately author pending work without an activity handler.
if ($revision === 'v2') {
$worker->registerActivity('polyglot.php.upgrade-activity',
static function (ActivityContext $context, array $request) use ($revision): array {
return ['activity_runtime' => 'php', 'application_revision' => $revision,
'sdk_version' => 'durable-workflow-php/'.InstalledVersions::getPrettyVersion('durable-workflow/sdk'),
'attempt_number' => $context->attemptNumber, 'request' => $request];
});
}
$worker->run(2);
Loading
Loading