Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add WorkflowUpdateRPCTimeoutOrCanceledException #490

Merged
merged 3 commits into from
Aug 19, 2024
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
5 changes: 5 additions & 0 deletions src/Client/GRPC/BaseClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
use Temporal\Client\Common\ServerCapabilities;
use Temporal\Client\GRPC\Connection\Connection;
use Temporal\Client\GRPC\Connection\ConnectionInterface;
use Temporal\Exception\Client\CanceledException;
use Temporal\Exception\Client\ServiceClientException;
use Temporal\Exception\Client\TimeoutException;
use Temporal\Interceptor\GrpcClientInterceptor;
Expand Down Expand Up @@ -302,6 +303,10 @@ private function call(string $method, object $arg, ContextInterface $ctx): objec
throw new TimeoutException($e->getMessage(), $e->getCode(), $e);
}

if ($e->getCode() === StatusCode::CANCELLED) {
throw new CanceledException($e->getMessage(), $e->getCode(), $e);
}

// non retryable
throw $e;
}
Expand Down
21 changes: 14 additions & 7 deletions src/Client/Update/UpdateHandle.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@
use Temporal\DataConverter\DataConverterInterface;
use Temporal\DataConverter\EncodedValues;
use Temporal\DataConverter\ValuesInterface;
use Temporal\Exception\Client\CanceledException;
use Temporal\Exception\Client\TimeoutException;
use Temporal\Exception\Client\WorkflowUpdateException;
use Temporal\Exception\Client\WorkflowUpdateRPCTimeoutOrCanceledException;
use Temporal\Exception\Failure\FailureConverter;
use Temporal\Workflow\WorkflowExecution;

Expand Down Expand Up @@ -70,7 +72,7 @@ public function hasResult(): bool
* @param int|float|null $timeout Timeout in seconds. Accuracy to milliseconds.
*
* @throws WorkflowUpdateException
* @throws TimeoutException
* @throws WorkflowUpdateRPCTimeoutOrCanceledException
*/
public function getResult(int|float|null $timeout = null): mixed
{
Expand All @@ -83,7 +85,7 @@ public function getResult(int|float|null $timeout = null): mixed
* @param int|float|null $timeout Timeout in seconds. Accuracy to milliseconds.
*
* @throws WorkflowUpdateException
* @throws TimeoutException
* @throws WorkflowUpdateRPCTimeoutOrCanceledException
*/
public function getEncodedValues(int|float|null $timeout = null): ValuesInterface
{
Expand All @@ -100,7 +102,7 @@ public function getEncodedValues(int|float|null $timeout = null): ValuesInterfac
* @param int|float|null $timeout Timeout in seconds. Accuracy to milliseconds.
*
* @psalm-assert !null $this->result
* @throws TimeoutException
* @throws WorkflowUpdateRPCTimeoutOrCanceledException
*/
private function fetchResult(int|float|null $timeout = null): void
{
Expand All @@ -116,10 +118,14 @@ private function fetchResult(int|float|null $timeout = null): void
(new \Temporal\Api\Update\V1\WaitPolicy())->setLifecycleStage(LifecycleStage::StageCompleted->value)
);

$response = $this->client->PollWorkflowExecutionUpdate(
$request,
$timeout === null ? null : $this->client->getContext()->withTimeout($timeout),
);
try {
$response = $this->client->PollWorkflowExecutionUpdate(
$request,
$timeout === null ? null : $this->client->getContext()->withTimeout($timeout),
);
} catch (TimeoutException|CanceledException $e) {
throw WorkflowUpdateRPCTimeoutOrCanceledException::fromTimeoutOrCanceledException($e);
}

// Workflow Uprate accepted
$result = $response->getOutcome();
Expand All @@ -135,6 +141,7 @@ private function fetchResult(int|float|null $timeout = null): void
$failure = $result->getFailure();
\assert($failure !== null);
$e = FailureConverter::mapFailureToException($failure, $this->converter);
tr($e);

$this->result = new WorkflowUpdateException(
$e->getMessage(),
Expand Down
6 changes: 5 additions & 1 deletion src/Client/WorkflowStubInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
use Temporal\Client\Update\UpdateOptions;
use Temporal\DataConverter\Type;
use Temporal\DataConverter\ValuesInterface;
use Temporal\Exception\Client\WorkflowUpdateException;
use Temporal\Exception\Client\WorkflowUpdateRPCTimeoutOrCanceledException;
use Temporal\Exception\IllegalStateException;
use Temporal\Workflow\CancellationScopeInterface;
use Temporal\Workflow\QueryMethod;
Expand Down Expand Up @@ -91,6 +93,8 @@ public function query(string $name, ...$args): ?ValuesInterface;
* @param non-empty-string $name Name of the update handler.
* @param mixed ...$args Arguments to pass to the update handler.
* @return ValuesInterface|null
* @throws WorkflowUpdateException
* @throws WorkflowUpdateRPCTimeoutOrCanceledException
*/
public function update(string $name, ...$args): ?ValuesInterface;

Expand All @@ -107,7 +111,7 @@ public function update(string $name, ...$args): ?ValuesInterface;
*
* @param non-empty-string|UpdateOptions $nameOrOptions Name of the update handler or update options.
* @param mixed ...$args Arguments to pass to the update handler.
* @return UpdateHandle
* @throws WorkflowUpdateRPCTimeoutOrCanceledException
*/
public function startUpdate(string|UpdateOptions $nameOrOptions, ...$args): UpdateHandle;

Expand Down
19 changes: 19 additions & 0 deletions src/Exception/Client/CanceledException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

/**
* This file is part of Temporal package.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace Temporal\Exception\Client;

use Temporal\Exception\TemporalException;

/**
* RPC call was canceled.
*/
class CanceledException extends TemporalException {}
7 changes: 4 additions & 3 deletions src/Exception/Client/TimeoutException.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

use Temporal\Exception\TemporalException;

class TimeoutException extends TemporalException
{
}
/**
* RPC timeout or cancellation.
*/
class TimeoutException extends TemporalException {}
2 changes: 1 addition & 1 deletion src/Exception/Client/WorkflowUpdateException.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

use Temporal\Workflow\WorkflowExecution;

final class WorkflowUpdateException extends WorkflowException
class WorkflowUpdateException extends WorkflowException
{
public function __construct(
?string $message,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

/**
* This file is part of Temporal package.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace Temporal\Exception\Client;

/**
* Occurs when an update call times out or is cancelled.
*
* @note this is not related to any general concept of timing out or cancelling a running update,
* this is only related to the client call itself.
*/
class WorkflowUpdateRPCTimeoutOrCanceledException extends TimeoutException {
private function __construct(string $message = '', int $code = 0, ?\Throwable $previous = null)
{
parent::__construct($message, $code, $previous);
}

public static function fromTimeoutOrCanceledException(TimeoutException|CanceledException $exception): self
{
return new self(
previous: $exception->getPrevious(),
);
}
}
3 changes: 3 additions & 0 deletions src/Internal/Client/WorkflowStub.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
use Temporal\Exception\Client\WorkflowQueryRejectedException;
use Temporal\Exception\Client\WorkflowServiceException;
use Temporal\Exception\Client\WorkflowUpdateException;
use Temporal\Exception\Client\WorkflowUpdateRPCTimeoutOrCanceledException;
use Temporal\Exception\Failure\CanceledFailure;
use Temporal\Exception\Failure\FailureConverter;
use Temporal\Exception\Failure\TerminatedFailure;
Expand Down Expand Up @@ -332,6 +333,8 @@ static function (
}

throw WorkflowServiceException::withoutMessage($input->workflowExecution, $input->workflowType, $e);
} catch (TimeoutException $e) {
throw WorkflowUpdateRPCTimeoutOrCanceledException::fromTimeoutOrCanceledException($e);
} catch (\Throwable $e) {
throw new WorkflowServiceException(null, $input->workflowExecution, $input->workflowType, $e);
}
Expand Down
2 changes: 1 addition & 1 deletion tests/Acceptance/App/Attribute/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
final class Client
{
public function __construct(
public int|string|null $timeout = null,
public float|null $timeout = null,
public \Closure|array|string|null $pipelineProvider = null,
public array $payloadConverters = [],
) {
Expand Down
75 changes: 75 additions & 0 deletions tests/Acceptance/Extra/Update/TimeoutTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<?php

declare(strict_types=1);

namespace Temporal\Tests\Acceptance\Extra\Update\TimeoutTest;

use PHPUnit\Framework\Attributes\Test;
use Temporal\Client\WorkflowClientInterface;
use Temporal\Client\WorkflowOptions;
use Temporal\Client\WorkflowStubInterface;
use Temporal\Exception\Client\WorkflowUpdateRPCTimeoutOrCanceledException;
use Temporal\Tests\Acceptance\App\Attribute\Client;
use Temporal\Tests\Acceptance\App\Attribute\Stub;
use Temporal\Tests\Acceptance\App\TestCase;
use Temporal\Workflow;
use Temporal\Workflow\WorkflowInterface;
use Temporal\Workflow\WorkflowMethod;

class TimeoutTest extends TestCase
{
#[Test]
public function getUpdateResultFromHandler(
#[Stub('Extra_Timeout_WorkflowUpdate')]
WorkflowStubInterface $stub,
): void {
/** @see TestWorkflow::sleep */
$handle = $stub->startUpdate('sleep', '1 second');

$this->expectException(WorkflowUpdateRPCTimeoutOrCanceledException::class);

$handle->getResult(0.2);
}

#[Test]
public function doUpdateWithTimeout(
#[Stub('Extra_Timeout_WorkflowUpdate')]
#[Client(timeout: 1.2)]
WorkflowStubInterface $stub,
): void {
$this->expectException(WorkflowUpdateRPCTimeoutOrCanceledException::class);

/** @see TestWorkflow::sleep */
$stub->update('sleep', '2 second');
}

#[Test]
public function withoutRunningWorker(WorkflowClientInterface $client): void
{
$client = $client->withTimeout(1.2);
$wf = $client->newUntypedWorkflowStub('Extra_Timeout_WorkflowUpdate', WorkflowOptions::new()
->withTaskQueue('not-existing-task-queue'));
$client->start($wf);

$this->expectException(WorkflowUpdateRPCTimeoutOrCanceledException::class);

/** @see TestWorkflow::sleep */
$wf->update('sleep', '2 second');
}
}

#[WorkflowInterface]
class TestWorkflow
{
#[WorkflowMethod(name: "Extra_Timeout_WorkflowUpdate")]
public function handle()
{
yield Workflow::await(static fn() => false);
}

#[Workflow\UpdateMethod(name: 'sleep')]
public function sleep(string $sleep): mixed
{
yield Workflow::timer(\DateInterval::createFromDateString($sleep));
}
}
Loading