Skip to content
Commits on Source (8)
#!/usr/bin/env php
<?php
###############################################################################################
# Moves the DbpRelayCoreBundle to bottom of the array in `config/bundles.php`.
###############################################################################################
//##############################################################################################
// Moves the DbpRelayCoreBundle to bottom of the array in `config/bundles.php`.
//##############################################################################################
// read the entire string
$str = file_get_contents('config/bundles.php');
$coreBundleString = " Dbp\Relay\CoreBundle\DbpRelayCoreBundle::class => ['all' => true],";
$str = str_replace($coreBundleString . "\n", '', $str);
$str = str_replace('];', $coreBundleString . "\n];", $str);
$str = str_replace($coreBundleString."\n", '', $str);
$str = str_replace('];', $coreBundleString."\n];", $str);
// write the entire string
file_put_contents('config/bundles.php', $str);
......@@ -3,7 +3,7 @@
"type": "symfony-bundle",
"license": "AGPL-3.0-or-later",
"require": {
"php": "^7.3",
"php": "^7.3 || ^8.0",
"ext-fileinfo": "*",
"ext-json": "*",
"api-platform/core": "^2.6.3",
......@@ -29,8 +29,8 @@
"require-dev": {
"brainmaestro/composer-git-hooks": "^2.8",
"friendsofphp/php-cs-fixer": "^3.0",
"phpstan/phpstan": "^0.12.33",
"phpstan/phpstan-phpunit": "^0.12.13",
"phpstan/phpstan": "^1.0.0",
"phpstan/phpstan-phpunit": "^1.0.0",
"symfony/browser-kit": "^5.3",
"symfony/http-client": "^5.3",
"symfony/phpunit-bridge": "^5.3",
......
......@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "83accfd991b38ee637acc3a0cade2f17",
"content-hash": "34f15df2aa0f926b4054d2e5b18d4a63",
"packages": [
{
"name": "api-platform/core",
......@@ -6623,16 +6623,16 @@
},
{
"name": "nikic/php-parser",
"version": "v4.13.0",
"version": "v4.13.1",
"source": {
"type": "git",
"url": "https://github.com/nikic/PHP-Parser.git",
"reference": "50953a2691a922aa1769461637869a0a2faa3f53"
"reference": "63a79e8daa781cac14e5195e63ed8ae231dd10fd"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/50953a2691a922aa1769461637869a0a2faa3f53",
"reference": "50953a2691a922aa1769461637869a0a2faa3f53",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/63a79e8daa781cac14e5195e63ed8ae231dd10fd",
"reference": "63a79e8daa781cac14e5195e63ed8ae231dd10fd",
"shasum": ""
},
"require": {
......@@ -6673,9 +6673,9 @@
],
"support": {
"issues": "https://github.com/nikic/PHP-Parser/issues",
"source": "https://github.com/nikic/PHP-Parser/tree/v4.13.0"
"source": "https://github.com/nikic/PHP-Parser/tree/v4.13.1"
},
"time": "2021-09-20T12:20:58+00:00"
"time": "2021-11-03T20:52:16+00:00"
},
{
"name": "openlss/lib-array2xml",
......@@ -6784,16 +6784,16 @@
},
{
"name": "phpstan/phpstan",
"version": "0.12.99",
"version": "1.1.1",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan.git",
"reference": "b4d40f1d759942f523be267a1bab6884f46ca3f7"
"reference": "cb317029197236c571c1b9305b8dd12850d8d85c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/b4d40f1d759942f523be267a1bab6884f46ca3f7",
"reference": "b4d40f1d759942f523be267a1bab6884f46ca3f7",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/cb317029197236c571c1b9305b8dd12850d8d85c",
"reference": "cb317029197236c571c1b9305b8dd12850d8d85c",
"shasum": ""
},
"require": {
......@@ -6809,7 +6809,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "0.12-dev"
"dev-master": "1.0-dev"
}
},
"autoload": {
......@@ -6824,7 +6824,7 @@
"description": "PHPStan - PHP Static Analysis Tool",
"support": {
"issues": "https://github.com/phpstan/phpstan/issues",
"source": "https://github.com/phpstan/phpstan/tree/0.12.99"
"source": "https://github.com/phpstan/phpstan/tree/1.1.1"
},
"funding": [
{
......@@ -6844,38 +6844,39 @@
"type": "tidelift"
}
],
"time": "2021-09-12T20:09:55+00:00"
"time": "2021-11-06T22:46:47+00:00"
},
{
"name": "phpstan/phpstan-phpunit",
"version": "0.12.22",
"version": "1.0.0",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan-phpunit.git",
"reference": "7c01ef93bf128b4ac8bdad38c54b2a4fd6b0b3cc"
"reference": "9eb88c9f689003a8a2a5ae9e010338ee94dc39b3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/7c01ef93bf128b4ac8bdad38c54b2a4fd6b0b3cc",
"reference": "7c01ef93bf128b4ac8bdad38c54b2a4fd6b0b3cc",
"url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/9eb88c9f689003a8a2a5ae9e010338ee94dc39b3",
"reference": "9eb88c9f689003a8a2a5ae9e010338ee94dc39b3",
"shasum": ""
},
"require": {
"php": "^7.1 || ^8.0",
"phpstan/phpstan": "^0.12.92"
"phpstan/phpstan": "^1.0"
},
"conflict": {
"phpunit/phpunit": "<7.0"
},
"require-dev": {
"nikic/php-parser": "^4.13.0",
"php-parallel-lint/php-parallel-lint": "^1.2",
"phpstan/phpstan-strict-rules": "^0.12.6",
"phpstan/phpstan-strict-rules": "^1.0",
"phpunit/phpunit": "^9.5"
},
"type": "phpstan-extension",
"extra": {
"branch-alias": {
"dev-master": "0.12-dev"
"dev-master": "1.0-dev"
},
"phpstan": {
"includes": [
......@@ -6896,9 +6897,9 @@
"description": "PHPUnit extensions and rules for PHPStan",
"support": {
"issues": "https://github.com/phpstan/phpstan-phpunit/issues",
"source": "https://github.com/phpstan/phpstan-phpunit/tree/0.12.22"
"source": "https://github.com/phpstan/phpstan-phpunit/tree/1.0.0"
},
"time": "2021-08-12T10:53:43+00:00"
"time": "2021-10-14T08:03:54+00:00"
},
{
"name": "sebastian/diff",
......@@ -7577,16 +7578,16 @@
},
{
"name": "vimeo/psalm",
"version": "4.11.2",
"version": "4.12.0",
"source": {
"type": "git",
"url": "https://github.com/vimeo/psalm.git",
"reference": "6fba5eb554f9507b72932f9c75533d8af593688d"
"reference": "e42bc4a23f67acba28a23bb09c348e2ff38a1d87"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/vimeo/psalm/zipball/6fba5eb554f9507b72932f9c75533d8af593688d",
"reference": "6fba5eb554f9507b72932f9c75533d8af593688d",
"url": "https://api.github.com/repos/vimeo/psalm/zipball/e42bc4a23f67acba28a23bb09c348e2ff38a1d87",
"reference": "e42bc4a23f67acba28a23bb09c348e2ff38a1d87",
"shasum": ""
},
"require": {
......@@ -7610,7 +7611,7 @@
"openlss/lib-array2xml": "^1.0",
"php": "^7.1|^8",
"sebastian/diff": "^3.0 || ^4.0",
"symfony/console": "^3.4.17 || ^4.1.6 || ^5.0",
"symfony/console": "^3.4.17 || ^4.1.6 || ^5.0 || ^6.0",
"webmozart/path-util": "^2.3"
},
"provide": {
......@@ -7628,7 +7629,7 @@
"psalm/plugin-phpunit": "^0.16",
"slevomat/coding-standard": "^7.0",
"squizlabs/php_codesniffer": "^3.5",
"symfony/process": "^4.3 || ^5.0",
"symfony/process": "^4.3 || ^5.0 || ^6.0",
"weirdan/prophecy-shim": "^1.0 || ^2.0"
},
"suggest": {
......@@ -7676,9 +7677,9 @@
],
"support": {
"issues": "https://github.com/vimeo/psalm/issues",
"source": "https://github.com/vimeo/psalm/tree/4.11.2"
"source": "https://github.com/vimeo/psalm/tree/4.12.0"
},
"time": "2021-10-26T17:28:17+00:00"
"time": "2021-11-06T10:31:17+00:00"
},
{
"name": "webmozart/path-util",
......@@ -7728,6 +7729,7 @@
"issues": "https://github.com/webmozart/path-util/issues",
"source": "https://github.com/webmozart/path-util/tree/2.3.0"
},
"abandoned": "symfony/filesystem",
"time": "2015-12-17T08:42:14+00:00"
}
],
......@@ -7737,7 +7739,7 @@
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
"php": "^7.3",
"php": "^7.3 || ^8.0",
"ext-fileinfo": "*",
"ext-json": "*"
},
......
# Locks
Locks are used to provide exclusive access to a shared resource.
The Relay API gateway optionally requires a locking backend. By default it uses
a local backend which is only suitable if all processes run on the same server.
Once your API runs on multiple servers you need to configure a remote/shared
locking backend.
## Configuration
In the bundle configuration set the `lock_dsn` key to a DSN supported by the
[Symfony Lock component](https://symfony.com/doc/current/components/lock.html)
At the moment we only support RedisStore and PdoStore:
### Redis
```yaml
# config/packages/dbp_relay_core.yaml
dbp_relay_core:
# redis[s]://[pass@][ip|host|socket[:port]]
lock_dsn: 'redis://localhost:6379'
```
### PDO
```yaml
# config/packages/dbp_relay_core.yaml
dbp_relay_core:
lock_dsn: 'mysql://user:secret@mariadb:3306/dbname'
```
This will create a `lock_keys` table in your database where the lock information
will be stored.
## Usage in Code
```php
// Retrieve a LockFactory instance via dependency injection
public function __construct(LockFactory $lockFactory) {
// Make sure to prefix the resource string to avoid conflicts with other bundles
$lock = $lockFactory->createLock(/* ... */);
// ...
}
```
For more information see
https://symfony.com/doc/current/components/lock.html#usage
\ No newline at end of file
......@@ -16,14 +16,48 @@ This requires two extra deployment related tasks:
In the bundle configuration set the `queue_dsn` key to a DSN supported by the
[Symfony messenger component](https://symfony.com/doc/current/messenger.html)
At the moment we only support the redis transport.
At the moment we only support the redis and doctrine transports:
Example:
### Redis
This transport requires the Redis PHP extension (>=4.3) and a running Redis server (^5.0).
```yaml
# config/packages/dbp_relay_core.yaml
dbp_relay_core:
# redis[s]://[pass@][ip|host|socket[:port]]
queue_dsn: 'redis://localhost:6379'
```
This creates a redis stream automatically when active.
### Doctrine
In case of doctrine you have to install `symfony/doctrine-messenger`
```bash
composer require symfony/doctrine-messenger
```
then create a doctrine connection and point the `queue_dsn` to that connection:
```yaml
queue_dsn: 'redis://localhost:6379'
# config/packages/doctrine.yaml
doctrine:
dbal:
connections:
my-queue-connection-name:
url: 'mysql://db:secret@mariadb:3306/db'
```
```yaml
# config/packages/dbp_relay_core.yaml
dbp_relay_core:
queue_dsn: 'doctrine://my-queue-connection-name'
```
I will automatically create a new database table when active.
## Run the workers
Start a worker using
......@@ -76,4 +110,13 @@ autorestart=true
process_name=%(program_name)s_%(process_num)02d
```
Change `user` to the Unix user on your server.
\ No newline at end of file
Change `user` to the Unix user on your server.
## Testing your Setup
After everything is set up you can create a few dummy tasks and see if they get
handled by the workers:
```bash
./bin/console dbp:relay:core:queue:test --count 10 --delay 3
```
......@@ -9,7 +9,7 @@ parameters:
- tests
bootstrapFiles:
- vendor/bin/.phpunit/phpunit-9-0/vendor/autoload.php
excludes_analyse:
excludePaths:
- tests/bootstrap.php
- src/Swagger/DocumentationNormalizer.php
ignoreErrors:
......
......@@ -4,6 +4,8 @@ declare(strict_types=1);
namespace Dbp\Relay\CoreBundle\DependencyInjection;
use Dbp\Relay\CoreBundle\Queue\TestMessage;
use Dbp\Relay\CoreBundle\Queue\Utils as QueueUtils;
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
......@@ -163,6 +165,10 @@ class DbpRelayCoreExtension extends ConfigurableExtension implements PrependExte
]),
]);
$routing = [
TestMessage::class => QueueUtils::QUEUE_TRANSPORT_NAME,
];
// https://symfony.com/doc/4.4/messenger.html#transports-async-queued-messages
$messengerTransportDsn = $config['queue_dsn'];
if ($messengerTransportDsn === '') {
......@@ -170,36 +176,28 @@ class DbpRelayCoreExtension extends ConfigurableExtension implements PrependExte
$messengerTransportDsn = $config['messenger_transport_dsn'];
}
if ($container->hasParameter('dbp_api.messenger_routing')) {
$routing = [];
$routing = array_merge($routing, $container->getParameter('dbp_api.messenger_routing'));
if ($messengerTransportDsn === '') {
throw new \RuntimeException('A bundle requires a worker queue: set "queue_dsn" in the core bundle config');
}
$container->loadFromExtension('framework', [
'messenger' => [
'transports' => [
'async' => $messengerTransportDsn,
],
'routing' => $routing,
],
]);
} else {
// By always setting a transport, we ensure that the messenger commands work in all cases, even if they
// are not stricly needed
if ($messengerTransportDsn === '') {
$messengerTransportDsn = 'in-memory://dummy-queue-not-configured';
}
$container->loadFromExtension('framework', [
'messenger' => [
'transports' => [
'async' => $messengerTransportDsn,
],
],
]);
}
$container->loadFromExtension('framework', [
'messenger' => [
'transports' => [
QueueUtils::QUEUE_TRANSPORT_NAME => $messengerTransportDsn,
],
'routing' => $routing,
],
]);
// https://symfony.com/doc/5.3/components/lock.html
$lockDsn = $config['lock_dsn'];
if ($lockDsn !== '') {
......
<?php
declare(strict_types=1);
namespace Dbp\Relay\CoreBundle\Queue;
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerAwareTrait;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Messenger\Envelope;
use Symfony\Component\Messenger\MessageBusInterface;
use Symfony\Component\Messenger\Stamp\DelayStamp;
class TestCommand extends Command implements LoggerAwareInterface
{
use LoggerAwareTrait;
protected static $defaultName = 'dbp:relay:core:queue:test';
/**
* @var MessageBusInterface
*/
private $bus;
public function __construct(MessageBusInterface $bus)
{
parent::__construct();
$this->bus = $bus;
}
protected function configure()
{
$this->setDescription('Start some dummy tasks for testing');
$this->addOption('count', null, InputArgument::OPTIONAL, 'The number of messages to send', 1);
$this->addOption('delay', null, InputArgument::OPTIONAL, 'Delay in seconds', 0);
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$count = (int) $input->getOption('count');
$delay = (int) $input->getOption('delay');
for ($i = 0; $i < $count; ++$i) {
if ($delay !== 0) {
$envelope = new Envelope(new TestMessage(), [new DelayStamp($delay * 1000)]);
} else {
$envelope = new Envelope(new TestMessage());
}
$this->bus->dispatch($envelope);
}
return Command::SUCCESS;
}
}
<?php
declare(strict_types=1);
namespace Dbp\Relay\CoreBundle\Queue;
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerAwareTrait;
use Symfony\Component\Messenger\Handler\MessageHandlerInterface;
class TestHandler implements MessageHandlerInterface, LoggerAwareInterface
{
use LoggerAwareTrait;
public function __invoke(TestMessage $message)
{
$this->logger->info('Handled test message');
}
}
<?php
declare(strict_types=1);
namespace Dbp\Relay\CoreBundle\Queue;
class TestMessage
{
}
......@@ -56,10 +56,13 @@ class TransportFactoryDecorator implements TransportFactoryInterface, LoggerAwar
// Use the new recommended default:
// https://github.com/symfony/symfony/pull/42163
$options['delete_after_ack'] = true;
} elseif (strpos($dsn, 'doctrine://') === 0) {
$options['table_name'] = 'core_queue_messages';
$options['queue_name'] = 'main';
} elseif ($dsn === 'in-memory://dummy-queue-not-configured') {
// This is used when no queue is configured, so allow it.
} else {
throw new \Exception('Only redis currently supported as a messenger transport (current DSN: '.$dsn.')');
throw new \Exception('Only redis and doctrine currently supported as a queue transport (current DSN: '.$dsn.')');
}
}
......
......@@ -29,5 +29,13 @@ services:
autoconfigure: true
Dbp\Relay\CoreBundle\Queue\RestartCommand:
autowire: true
autoconfigure: true
Dbp\Relay\CoreBundle\Queue\TestCommand:
autowire: true
autoconfigure: true
Dbp\Relay\CoreBundle\Queue\TestHandler:
autowire: true
autoconfigure: true
\ No newline at end of file