Skip to content

Commit

Permalink
Add Workflow Worker related Factory
Browse files Browse the repository at this point in the history
  • Loading branch information
roxblnfk committed Jan 14, 2024
1 parent e293e17 commit 62ffd45
Show file tree
Hide file tree
Showing 3 changed files with 267 additions and 18 deletions.
47 changes: 47 additions & 0 deletions src/Internal/RetryOptions.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php

declare(strict_types=1);

namespace Temporal\Sugar\Internal;

use DateInterval;
use Throwable;

/**
* @internal
*/
final class RetryOptions
{
/**
* @param int<0, max> $retryAttempts Maximum number of attempts. When exceeded the retries stop even
* if not expired yet. If not set or set to 0, it means unlimited, and rely on activity
* {@see ActivityOptions::$scheduleToCloseTimeout} to stop.
* @param DateInterval|string|int|null $retryInitInterval Backoff interval for the first retry.
* If $retryBackoff is 1.0 then it is used for all retries.
* Int value in seconds.
* @param DateInterval|string|int|null $retryMaxInterval Maximum backoff interval between retries.
* Exponential backoff leads to interval increase. This value is the cap of the interval.
* Int value in seconds.
* Default is 100x of $retryInitInterval.
* @param float|null $retryBackoff Coefficient used to calculate the next retry backoff interval.
* The next retry interval is previous interval multiplied by this coefficient.
* Note: Must be greater than 1.0
* @param list<class-string<Throwable>> $nonRetryables Non-retriable errors. Temporal server will stop retry
* if error type matches this list.
*/
public static function create(
int $retryAttempts = 0,
\DateInterval|string|int|null $retryInitInterval = null,
\DateInterval|string|int|null $retryMaxInterval = null,
?float $retryBackoff = null,
array $nonRetryables = [],
): \Temporal\Common\RetryOptions {
$retryOptions = \Temporal\Common\RetryOptions::new();
$retryAttempts === 0 or $retryOptions = $retryOptions->withMaximumAttempts($retryAttempts);
$retryInitInterval === 0 or $retryOptions = $retryOptions->withInitialInterval($retryInitInterval);
$retryMaxInterval === 0 or $retryOptions = $retryOptions->withMaximumInterval($retryMaxInterval);
$retryBackoff >= 1.0 and $retryOptions = $retryOptions->withBackoffCoefficient($retryBackoff);
$nonRetryables === 0 or $retryOptions = $retryOptions->withNonRetryableExceptions($nonRetryables);
return $retryOptions;
}
}
37 changes: 19 additions & 18 deletions src/Factory/ClientWorkflow.php → src/Stub/ClientFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,20 @@

declare(strict_types=1);

namespace Temporal\Sugar\Factory;
namespace Temporal\Sugar\Stub;

use DateInterval;
use Temporal\Client\WorkflowClientInterface;
use Temporal\Client\WorkflowOptions;
use Temporal\Common\IdReusePolicy;
use Temporal\Common\RetryOptions;
use Temporal\Internal\Client\WorkflowProxy;
use Temporal\Sugar\Internal\RetryOptions;
use Throwable;

/**
* Note: mustn't be used in a Workflow context.
*/
final class ClientWorkflow
final class ClientFactory
{
/**
* @template T of object
Expand All @@ -38,27 +38,27 @@ final class ClientWorkflow
* @param list<class-string<Throwable>> $nonRetryables Non-retriable errors. Temporal server will stop retry
* if error type matches this list.
* @param DateInterval|string|int $executionTimeout The maximum time that parent workflow is willing to wait
* for a child execution (which includes retries and continue as new calls).
* If exceeded the child is automatically terminated by the Temporal service.
* Int value in seconds.
* for a child execution (which includes retries and continue as new calls).
* If exceeded the child is automatically terminated by the Temporal service.
* Int value in seconds.
* @param DateInterval|string|int $runTimeout The time after which workflow run is automatically terminated by
* the Temporal service.
* Do not rely on the run timeout for business level timeouts.
* It is preferred to use in workflow timers for this purpose.
* Int value in seconds.
* @param int<10, 60> $taskTimeout Maximum execution time of a single workflow task. Int value in seconds.
* Default is 10 seconds. The maximum accepted value is 60 seconds.
* @param DateInterval|string|int $startDelay Time to wait before dispatching the first Workflow task.
* If the Workflow gets a Signal before the delay, a Workflow task will be dispatched and the rest
* of the delay will be ignored. A Signal from {@see WorkflowClientInterface::startWithSignal()}
* won't trigger a workflow task. Cannot be set the same time as a $cronSchedule.
* Int value in seconds.
* @param int<10, 60> $taskTimeout Maximum execution time of a single workflow task. Int value in seconds.
* Default is 10 seconds. The maximum accepted value is 60 seconds.
* @param bool $eagerStart Eager Workflow Dispatch is a mechanism that minimizes the duration from
* starting a workflow to the processing of the first workflow task, making Temporal more suitable
* for latency sensitive applications.
* Eager Workflow Dispatch can be enabled if the server supports it and a local worker is available
* the task is fed directly to the worker.
* @param string|null $workflowId If null, then UUID will be generated.
* @param \Stringable|string|null $workflowId If null, then UUID will be generated.
* @param string|null $cronSchedule Optional cron schedule for workflow. {@see CronSchedule::$interval} for
* more info about cron format.
* @param array<non-empty-string, mixed> $searchAttributes Specifies additional indexed information in result
Expand All @@ -67,7 +67,7 @@ final class ClientWorkflow
*
* @return T|WorkflowProxy
*/
public static function workflowStub(
public static function workflow(
WorkflowClientInterface $workflowClient,
string $workflow,
?string $taskQueue = null,
Expand All @@ -81,19 +81,20 @@ public static function workflowStub(
int $taskTimeout = 10,
\DateInterval|string|int $startDelay = 0,
bool $eagerStart = false,
?string $workflowId = null,
\Stringable|string|null $workflowId = null,
IdReusePolicy $workflowIdReusePolicy = IdReusePolicy::Unspecified,
?string $cronSchedule = null,
array $searchAttributes = [],
array $memo = [],
): object {
// Retry options
$retryOptions = RetryOptions::new();
$retryAttempts === 0 or $retryOptions = $retryOptions->withMaximumAttempts($retryAttempts);
$retryInitInterval === 0 or $retryOptions = $retryOptions->withInitialInterval($retryInitInterval);
$retryMaxInterval === 0 or $retryOptions = $retryOptions->withMaximumInterval($retryMaxInterval);
$retryBackoff >= 1.0 and $retryOptions = $retryOptions->withBackoffCoefficient($retryBackoff);
$nonRetryables === 0 or $retryOptions = $retryOptions->withNonRetryableExceptions($nonRetryables);
$retryOptions = RetryOptions::create(
$retryAttempts,
$retryInitInterval,
$retryMaxInterval,
$retryBackoff,
$nonRetryables,
);

$options = WorkflowOptions::new()->withRetryOptions($retryOptions);
$taskQueue === null or $options = $options->withTaskQueue($taskQueue);
Expand All @@ -106,7 +107,7 @@ public static function workflowStub(
$runTimeout === 0 or $options = $options->withWorkflowRunTimeout($executionTimeout);
$taskTimeout > 10 and $options = $options->withWorkflowTaskTimeout(\max(60, $taskTimeout));
// Workflow ID
$workflowId === null or $options = $options->withWorkflowId($workflowId);
$workflowId === null or $options = $options->withWorkflowId((string)$workflowId);
$workflowIdReusePolicy === IdReusePolicy::Unspecified or $options = $options
->withWorkflowIdReusePolicy($workflowIdReusePolicy);
// Metadata
Expand Down
201 changes: 201 additions & 0 deletions src/Stub/WorkflowFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
<?php

declare(strict_types=1);

namespace Temporal\Sugar\Stub;

use DateInterval;
use Temporal\Activity\ActivityOptions;
use Temporal\Common\IdReusePolicy;
use Temporal\Internal\Workflow\ActivityProxy;
use Temporal\Internal\Workflow\ChildWorkflowProxy;
use Temporal\Sugar\Internal\RetryOptions;
use Temporal\Workflow;
use Temporal\Workflow\ChildWorkflowCancellationType as ChildCancelType;
use Temporal\Workflow\ParentClosePolicy;
use Throwable;

/**
* Note: must be used in Workflow context only.
*/
final class WorkflowFactory
{
/**
* @template T of object
*
* @param class-string<T> $workflow
* @param non-empty-string|null $taskQueue Task queue to use for workflow tasks. It should match a task queue
* specified when creating a {@see Worker} that hosts the workflow code.
* @param non-empty-string|null $namespace Specify namespace in which workflow should be started.
* @param int<0, max> $retryAttempts Maximum number of attempts. When exceeded the retries stop even
* if not expired yet. If not set or set to 0, it means unlimited, and rely on activity
* {@see ActivityOptions::$scheduleToCloseTimeout} to stop.
* @param DateInterval|string|int|null $retryInitInterval Backoff interval for the first retry.
* If $retryBackoff is 1.0 then it is used for all retries.
* Int value in seconds.
* @param DateInterval|string|int|null $retryMaxInterval Maximum backoff interval between retries.
* Exponential backoff leads to interval increase. This value is the cap of the interval.
* Int value in seconds.
* Default is 100x of $retryInitInterval.
* @param float|null $retryBackoff Coefficient used to calculate the next retry backoff interval.
* The next retry interval is previous interval multiplied by this coefficient.
* Note: Must be greater than 1.0
* @param list<class-string<Throwable>> $nonRetryables Non-retriable errors. Temporal server will stop retry
* if error type matches this list.
* @param DateInterval|string|int $executionTimeout The maximum time that parent workflow is willing to wait
* for a child execution (which includes retries and continue as new calls).
* If exceeded the child is automatically terminated by the Temporal service.
* Int value in seconds.
* @param DateInterval|string|int $runTimeout The time after which workflow run is automatically terminated by
* the Temporal service.
* Do not rely on the run timeout for business level timeouts.
* It is preferred to use in workflow timers for this purpose.
* Int value in seconds.
* @param int<10, 60> $taskTimeout Maximum execution time of a single workflow task. Int value in seconds.
* Default is 10 seconds. The maximum accepted value is 60 seconds.
* @param ParentClosePolicy::POLICY_* $parentClosePolicy
* @param ChildCancelType::WAIT_CANCELLATION_COMPLETED|ChildCancelType::TRY_CANCEL $childCancellationType In case
* of a child workflow cancellation it fails with a {@see FailedCancellationException}.
* The type defines at which point the exception is thrown.
* @param \Stringable|string|null $workflowId If null, then UUID will be generated.
* @param string|null $cronSchedule Optional cron schedule for workflow. {@see CronSchedule::$interval} for
* more info about cron format.
* @param array<non-empty-string, mixed> $searchAttributes Specifies additional indexed information in result
* of list workflow.
* @param list<mixed> $memo Specifies additional non-indexed information in result of list workflow.
*
* @return T|ChildWorkflowProxy
*/
public static function childWorkflow(
string $workflow,
?string $taskQueue = null,
?string $namespace = null,
int $retryAttempts = 0,
\DateInterval|string|int|null $retryInitInterval = null,
\DateInterval|string|int|null $retryMaxInterval = null,
?float $retryBackoff = null,
array $nonRetryables = [],
\DateInterval|string|int $executionTimeout = 0,
\DateInterval|string|int $runTimeout = 0,
int $taskTimeout = 10,
int $parentClosePolicy = ParentClosePolicy::POLICY_UNSPECIFIED,
int $childCancellationType = ChildCancelType::TRY_CANCEL,
\Stringable|string|null $workflowId = null,
IdReusePolicy $workflowIdReusePolicy = IdReusePolicy::Unspecified,
?string $cronSchedule = null,
array $searchAttributes = [],
array $memo = [],
): object {
// Retry options
$retryOptions = RetryOptions::create(
$retryAttempts,
$retryInitInterval,
$retryMaxInterval,
$retryBackoff,
$nonRetryables,
);

$options = Workflow\ChildWorkflowOptions::new()->withRetryOptions($retryOptions);

$taskQueue === null or $options = $options->withTaskQueue($taskQueue);
$namespace === null or $options = $options->withNamespace($namespace);
// Start and close options
$cronSchedule === null or $options = $options->withCronSchedule($cronSchedule);
$parentClosePolicy === ParentClosePolicy::POLICY_UNSPECIFIED or $options = $options
->withParentClosePolicy($parentClosePolicy);
$childCancellationType === ChildCancelType::TRY_CANCEL or $options = $options
->withChildWorkflowCancellationType($childCancellationType);

// Timeouts
$executionTimeout === 0 or $options = $options->withWorkflowExecutionTimeout($executionTimeout);
$runTimeout === 0 or $options = $options->withWorkflowRunTimeout($executionTimeout);
$taskTimeout > 10 and $options = $options->withWorkflowTaskTimeout(\max(60, $taskTimeout));
// Workflow ID
$workflowId === null or $options = $options->withWorkflowId((string)$workflowId);
$workflowIdReusePolicy === IdReusePolicy::Unspecified or $options = $options
->withWorkflowIdReusePolicy($workflowIdReusePolicy);
// Metadata
$searchAttributes === [] or $options = $options->withSearchAttributes($searchAttributes);
$memo === [] or $options = $options->withMemo($memo);

return Workflow::newChildWorkflowStub($workflow, $options);
}

/**
* @template T of object
*
* @param class-string<T> $activity
* @param non-empty-string|null $taskQueue
* @param int<0, max> $retryAttempts Maximum number of attempts. When exceeded the retries stop even
* if not expired yet. If not set or set to 0, it means unlimited, and rely on activity
* {@see ActivityOptions::$scheduleToCloseTimeout} to stop.
* @param DateInterval|string|int|null $retryInitInterval Backoff interval for the first retry.
* If $retryBackoff is 1.0 then it is used for all retries.
* Int value in seconds.
* @param DateInterval|string|int|null $retryMaxInterval Maximum backoff interval between retries.
* Exponential backoff leads to interval increase. This value is the cap of the interval.
* Int value in seconds.
* Default is 100x of $retryInitInterval.
* @param float|null $retryBackoff Coefficient used to calculate the next retry backoff interval.
* The next retry interval is previous interval multiplied by this coefficient.
* Note: Must be greater than 1.0
* @param list<class-string<Throwable>> $nonRetryables Non-retriable errors. Temporal server will stop retry
* if error type matches this list.
* @param DateInterval|string|int $scheduleToStartTimeout Time activity can stay in task queue before it
* is picked up by a worker. If $scheduleToCloseTimeout is not provided then
* both this and $startToCloseTimeout are required.
* @param DateInterval|string|int $startToCloseTimeout Maximum activity execution time after it was sent
* to a worker. If $scheduleToCloseTimeout is not provided then both this
* and $scheduleToStartTimeout are required.
* @param DateInterval|string|int $scheduleToCloseTimeout Overall timeout workflow is willing to wait for
* activity to complete. It includes time in a task queue ($scheduleToStartTimeout) plus activity
* execution time ($startToCloseTimeout).
* Either this option or both $scheduleToStartTimeout and $startToCloseTimeout are required.
* @param DateInterval|string|int $heartbeatTimeout
* @param \Stringable|non-empty-string|null $activityId Business level activity ID, this is not needed
* for most of the cases. If you have to specify this, then talk to the temporal team.
* This is something will be done in the future.
* @param int $cancellationType Whether to wait for canceled activity to be completed (activity can be failed,
* completed, cancel accepted). {@see \Temporal\Activity\ActivityCancellationType}
*
* @return T|ActivityProxy
*/
public static function activity(
string $activity,
?string $taskQueue = null,
int $retryAttempts = 0,
\DateInterval|string|int|null $retryInitInterval = null,
\DateInterval|string|int|null $retryMaxInterval = null,
?float $retryBackoff = null,
array $nonRetryables = [],
\DateInterval|string|int $scheduleToStartTimeout = 0,
\DateInterval|string|int $startToCloseTimeout = 0,
\DateInterval|string|int $scheduleToCloseTimeout = 0,
\DateInterval|string|int $heartbeatTimeout = 0,
\Stringable|string|null $activityId = null,
int $cancellationType = 0,
): object {
// Retry options
$retryOptions = RetryOptions::create(
$retryAttempts,
$retryInitInterval,
$retryMaxInterval,
$retryBackoff,
$nonRetryables,
);

$options = ActivityOptions::new()->withRetryOptions($retryOptions);

$taskQueue === null or $options = $options->withTaskQueue($taskQueue);
// Timeouts
$scheduleToStartTimeout === 0 or $options = $options->withScheduleToStartTimeout($scheduleToStartTimeout);
$startToCloseTimeout === 0 or $options = $options->withStartToCloseTimeout($startToCloseTimeout);
$scheduleToCloseTimeout === 0 or $options = $options->withScheduleToCloseTimeout($scheduleToCloseTimeout);
$heartbeatTimeout === 0 or $options = $options->withHeartbeatTimeout($heartbeatTimeout);
// Activity ID
$activityId === null or $options = $options->withActivityId((string)$activityId);
$cancellationType === null or $options = $options->withCancellationType($cancellationType);

return Workflow::newActivityStub($activity, $options);
}
}

0 comments on commit 62ffd45

Please sign in to comment.