-
-
Notifications
You must be signed in to change notification settings - Fork 59
Feat: temporal worker #166
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
base: 3.x
Are you sure you want to change the base?
Changes from all commits
65d1089
0d4e562
7e8ef45
a7ab7d5
029af5c
f960407
23c05a0
a71542a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -93,7 +93,7 @@ baldinof_road_runner: | |||||
| kernel_reboot: | ||||||
| strategy: max_jobs | ||||||
| max_jobs: 1000 # maximum number of request | ||||||
| max_jobs_dispersion: 0.2 # dispersion 20% used to prevent simultaneous reboot of all active workers (kernel will rebooted between 800 and 1000 requests) | ||||||
| max_jobs_dispersion: 0.2 # dispersion 20% used to prevent simultaneous reboot of all active workers (kernel will rebooted between 800 and 1000 requests) | ||||||
| ``` | ||||||
|
|
||||||
| You can combine reboot strategies: | ||||||
|
|
@@ -219,9 +219,9 @@ class Calculator implements CalculatorInterface | |||||
|
|
||||||
| ## KV caching | ||||||
|
|
||||||
| Roadrunner has a KV (Key-Value) plugin that can be used to cache data between requests. | ||||||
| Roadrunner has a KV (Key-Value) plugin that can be used to cache data between requests. | ||||||
|
|
||||||
| To use it, refer to the configuration reference at https://roadrunner.dev/docs/kv-overview. | ||||||
| To use it, refer to the configuration reference at https://roadrunner.dev/docs/kv-overview. | ||||||
| This requires the `spiral/roadrunner-kv`, `spiral/goridge` and `symfony/cache` composer dependencies. Basic configuration example: | ||||||
|
|
||||||
| Example configuration: | ||||||
|
|
@@ -260,6 +260,139 @@ framework: | |||||
| adapter: cache.adapter.roadrunner.kv_example | ||||||
| ``` | ||||||
|
|
||||||
| ## Temporal Integration | ||||||
|
|
||||||
| Ability to serve temporal request from roadrunner and provide temporal client. | ||||||
|
|
||||||
| To enable integration, please install `ext-grpc` and `temporal/sdk` | ||||||
|
|
||||||
| ```bash | ||||||
| composer require temporal/sdk | ||||||
| ``` | ||||||
|
|
||||||
| Complete list of configuration | ||||||
|
|
||||||
| ```yaml | ||||||
| baldinof_road_runner: | ||||||
| temporal: | ||||||
| data_converters: | ||||||
| - Temporal\DataConverter\NullConverter | ||||||
| - Temporal\DataConverter\BinaryConverter | ||||||
| - Temporal\DataConverter\ProtoJsonConverter | ||||||
| - Temporal\DataConverter\JsonConverter | ||||||
| default_client: default | ||||||
| clients: | ||||||
| default: | ||||||
| namespace: default | ||||||
| address: 'localhost:7233' | ||||||
| crt: <string> | ||||||
| client_key: <string> | ||||||
| client_pem: <sstring> | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| override_server_name: <string> | ||||||
| identity: <string> | ||||||
| interceptors: { } # array of service id | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| query_reject_condition: <enum> | ||||||
| workers: | ||||||
| default: | ||||||
| queue: default | ||||||
| exception_interceptor: temporal.exception_interceptor | ||||||
| default_interceptors: true | ||||||
| options: | ||||||
| max_concurrent_activity_execution_size: <int> | ||||||
| worker_activities_per_second: <float> | ||||||
| max_concurrent_local_activity_execution_size: <int> | ||||||
| worker_local_activities_per_second: <int> | ||||||
| task_queue_activities_per_second: <int> | ||||||
| max_concurrent_activity_task_pollers: <int> | ||||||
| max_concurrent_workflow_task_execution_size: <int> | ||||||
| max_concurrent_workflow_task_pollers: <int> | ||||||
| sticky_schedule_to_start_timeout: <int> | ||||||
| worker_stop_timeout: <int> | ||||||
| enable_session_worker: <bool> | ||||||
| session_resource_id: <string> | ||||||
| max_concurrent_session_execution_size: <int> | ||||||
| interceptors: { } # array of service id | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if it's array, it should be array 🙂
Suggested change
|
||||||
| ``` | ||||||
|
|
||||||
| To register a workflow and activity, you just need to tag actual class with `#[WorkflowInterface]` and `#[ActivityInterface]` | ||||||
|
|
||||||
| ```php | ||||||
| <?php | ||||||
|
|
||||||
| declare(strict_types=1); | ||||||
|
|
||||||
| namespace App\Temporal; | ||||||
|
|
||||||
| use Temporal\Activity\ActivityOptions; | ||||||
| use Temporal\Workflow; | ||||||
| use Temporal\Workflow\WorkflowInterface; | ||||||
| use Temporal\Workflow\WorkflowMethod; | ||||||
|
|
||||||
| #[WorkflowInterface] | ||||||
| class ExampleWorkflow | ||||||
|
Comment on lines
+331
to
+332
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. probably |
||||||
| { | ||||||
| /** | ||||||
| * @var ExampleActivity | ||||||
| */ | ||||||
| private $exampleActivity; | ||||||
|
|
||||||
| public function __construct() | ||||||
| { | ||||||
| $this->exampleActivity = Workflow::newActivityStub( | ||||||
| ExampleActivity::class, | ||||||
| ActivityOptions::new()->withStartToCloseTimeout(2000) | ||||||
| ); | ||||||
| } | ||||||
|
|
||||||
| #[WorkflowMethod] | ||||||
| public function greet(string $name): \Generator | ||||||
| { | ||||||
| return yield $this->exampleActivity->composeGreet($name); | ||||||
| } | ||||||
| } | ||||||
| ``` | ||||||
|
|
||||||
| ```php | ||||||
| <?php | ||||||
|
|
||||||
| declare(strict_types=1); | ||||||
|
|
||||||
| namespace App\Temporal; | ||||||
|
|
||||||
| use App\Service\MyService; | ||||||
| use Temporal\Activity\ActivityInterface; | ||||||
| use Temporal\Activity\ActivityMethod; | ||||||
|
|
||||||
| #[ActivityInterface] | ||||||
| class ExampleActivity | ||||||
| { | ||||||
| public function __construct(private MyService $myService) | ||||||
| { | ||||||
| } | ||||||
|
|
||||||
| #[ActivityMethod] | ||||||
| public function composeGreet(string $name): string | ||||||
| { | ||||||
| return "Hello {$name}!"; | ||||||
| } | ||||||
| } | ||||||
| ``` | ||||||
|
|
||||||
| Dependencies defined in the Activity constructor will be automatically resolved from the dependency injection container. | ||||||
|
|
||||||
| To use temporal default temporal client that listed in config you can type `WorkflowClientInterface` in constructor | ||||||
|
|
||||||
| ```php | ||||||
| final class Example | ||||||
| { | ||||||
| public function __constructor(private readonly WorkflowClientInterface $workflowClient) | ||||||
| {} | ||||||
| } | ||||||
| ``` | ||||||
|
|
||||||
| Additional clients are registered under the service id `temporal.client.$name`. | ||||||
|
|
||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would be pleasant to have the ability to debug stuff, similar to as it's already in vanta bundle:
|
||||||
|
|
||||||
| ## Usage with Docker | ||||||
|
|
||||||
| ```Dockerfile | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -35,7 +35,9 @@ | |
| "suggest": { | ||
| "nyholm/psr7": "For a super lightweight PSR-7/17 implementation", | ||
| "spiral/roadrunner-cli": "For easy installation of RoadRunner", | ||
| "symfony/proxy-manager-bridge": "For doctrine re-connection implementation" | ||
| "symfony/proxy-manager-bridge": "For doctrine re-connection implementation", | ||
| "temporal/sdk": "For using the temporal feature", | ||
| "ext-grpc": "When using a temporal client" | ||
| }, | ||
| "require-dev": { | ||
| "symfony/var-dumper": "^6.0 || ^7.0", | ||
|
|
@@ -57,7 +59,8 @@ | |
| "phpstan/phpstan": "^1.10", | ||
| "phpunit/phpunit": "^9.6", | ||
| "phpspec/prophecy": "^1.11", | ||
| "phpspec/prophecy-phpunit": "^2.0" | ||
| "phpspec/prophecy-phpunit": "^2.0", | ||
| "temporal/sdk": "^2.10" | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @Baldinof , since temporal sdk dependency is optional (required only in -dev), right now there are no constraints about client's version. In other words, roadrunner-bundle will be installed regardless of temporal/sdk version, which might not be compatible. I know of the following approach to this problem - one could write the required constraints in the "conflict" section so that even though dependency is not required, it'll still be bound by version constraints (in case it'll be required by the client code) |
||
| }, | ||
|
Comment on lines
+63
to
64
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is there any particular reason for not using |
||
| "conflict": { | ||
| "doctrine/doctrine-bundle": "<2.1.1", | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -36,6 +36,8 @@ | |||||
| use Spiral\RoadRunner\Worker as RoadRunnerWorker; | ||||||
| use Spiral\RoadRunner\WorkerInterface as RoadRunnerWorkerInterface; | ||||||
| use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; | ||||||
| use Temporal\Exception\ExceptionInterceptor; | ||||||
| use Temporal\Workflow\WorkflowInterface; | ||||||
|
|
||||||
| return static function (ContainerConfigurator $container) { | ||||||
| $container->parameters() | ||||||
|
|
@@ -149,4 +151,9 @@ | |||||
| service(InternalGrpcWorker::class), | ||||||
| ]); | ||||||
| } | ||||||
|
|
||||||
| if (class_exists(WorkflowInterface::class)) { | ||||||
| $services->set('temporal.exception_interceptor', ExceptionInterceptor::class) | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. shouldn't
Suggested change
|
||||||
| ->factory([ExceptionInterceptor::class, 'createDefault']); | ||||||
| } | ||||||
| }; | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,9 +8,12 @@ | |
| use Baldinof\RoadRunnerBundle\DependencyInjection\CompilerPass\InterceptorCompilerPass; | ||
| use Baldinof\RoadRunnerBundle\DependencyInjection\CompilerPass\MiddlewareCompilerPass; | ||
| use Baldinof\RoadRunnerBundle\DependencyInjection\CompilerPass\RemoveConfigureVarDumperListenerPass; | ||
| use Baldinof\RoadRunnerBundle\DependencyInjection\CompilerPass\TemporalCompilerPass; | ||
| use Spiral\RoadRunner\GRPC\ServiceInterface; | ||
| use Symfony\Component\DependencyInjection\ContainerBuilder; | ||
| use Symfony\Component\HttpKernel\Bundle\Bundle; | ||
| use Temporal\Client\WorkflowClientInterface; | ||
| use Temporal\Workflow\WorkflowInterface; | ||
|
|
||
| final class BaldinofRoadRunnerBundle extends Bundle | ||
| { | ||
|
|
@@ -24,5 +27,9 @@ public function build(ContainerBuilder $container): void | |
| $container->addCompilerPass(new GrpcServiceCompilerPass()); | ||
| $container->addCompilerPass(new InterceptorCompilerPass()); | ||
| } | ||
|
|
||
| if (interface_exists(WorkflowClientInterface::class) && class_exists(WorkflowInterface::class)) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think one check would be enough |
||
| $container->addCompilerPass(new TemporalCompilerPass()); | ||
| } | ||
| } | ||
| } | ||

There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be also good to have the ability to set grpc timeout (similar way to how it's in vanta bundle)