mirror of
https://github.com/alchemy-fr/Phraseanet.git
synced 2025-10-09 19:13:26 +00:00

* WIP/POC DO NOT MERGE use "pusher" to wait for export-by-email worker to tell client that export is done. nb: export worker is artificially delayed by 30s ! * WIP OK TO TEST ; DO NOT MERGE * WIP/POC DO NOT MERGE use "pusher" to wait for export-by-email worker to tell client that export is done. nb: export worker is artificially delayed by 30s ! * WIP OK TO TEST ; DO NOT MERGE * cleanup * cleanup * better conf & cleanup * fix typo * fix stamp transparency (bump imagine) fix missing cgu for one file download better cli feedback add (re)download link on cli * fix test * fix missing js feedback (when worker publish before client subscribes) * cleanup * fix "remove stamp" choice * add default conf * WIP/POC DO NOT MERGE use "pusher" to wait for export-by-email worker to tell client that export is done. nb: export worker is artificially delayed by 30s ! * WIP OK TO TEST ; DO NOT MERGE * WIP/POC DO NOT MERGE use "pusher" to wait for export-by-email worker to tell client that export is done. nb: export worker is artificially delayed by 30s ! * WIP OK TO TEST ; DO NOT MERGE * cleanup * cleanup * better conf & cleanup * fix typo * fix stamp transparency (bump imagine) fix missing cgu for one file download better cli feedback add (re)download link on cli * fix test * fix missing js feedback (when worker publish before client subscribes) * cleanup * fix "remove stamp" choice * add default conf * WIP OK TO TEST generates an excel report for async download. define some env-vars for Pusher (todo: fix entrypoint to add during install) * fix xl formating for tabs >1 add env_vars to config build * fix test
633 lines
23 KiB
PHP
633 lines
23 KiB
PHP
<?php
|
|
|
|
namespace Alchemy\Phrasea\WorkerManager\Queue;
|
|
|
|
use Alchemy\Phrasea\Core\Configuration\PropertyAccess;
|
|
use Exception;
|
|
use PhpAmqpLib\Channel\AMQPChannel;
|
|
use PhpAmqpLib\Connection\AMQPSSLConnection;
|
|
use PhpAmqpLib\Connection\AMQPStreamConnection;
|
|
use PhpAmqpLib\Wire\AMQPTable;
|
|
|
|
class AMQPConnection
|
|
{
|
|
const ALCHEMY_EXCHANGE = 'alchemy-exchange';
|
|
const RETRY_ALCHEMY_EXCHANGE = 'retry-alchemy-exchange';
|
|
|
|
/** @var AMQPStreamConnection */
|
|
private $connection;
|
|
/** @var AMQPChannel */
|
|
private $channel;
|
|
|
|
private $hostConfig;
|
|
private $conf;
|
|
|
|
// default message TTL in retry queue in millisecond
|
|
const DEFAULT_RETRY_DELAY_VALUE = 10000;
|
|
|
|
// default message TTL in delayed queue in millisecond
|
|
const DEFAULT_DELAYED_DELAY_VALUE = 5000;
|
|
|
|
// max number of retry before a message goes in failed
|
|
const DEFAULT_MAX_RETRY_VALUE = 3;
|
|
|
|
const WITH_NOTHING = 0;
|
|
const WITH_RETRY = 1;
|
|
const WITH_DELAYED = 2;
|
|
const WITH_LOOP = 4;
|
|
|
|
const BASE_QUEUE = 'base_q';
|
|
const BASE_QUEUE_WITH_RETRY = 'base_q_with_retry';
|
|
const BASE_QUEUE_WITH_LOOP = 'base_q_with_loop';
|
|
const RETRY_QUEUE = 'retry_q';
|
|
const LOOP_QUEUE = 'loop_q';
|
|
const FAILED_QUEUE = 'failed_q';
|
|
const DELAYED_QUEUE = 'delayed_q';
|
|
|
|
// settings names
|
|
const MAX_RETRY = 'max_retry';
|
|
const TTL_RETRY = 'ttl_retry';
|
|
const TTL_DELAYED = 'ttl_delayed';
|
|
|
|
const MESSAGES = [
|
|
MessagePublisher::ASSETS_INGEST_TYPE => [
|
|
'with' => self::WITH_RETRY,
|
|
self::MAX_RETRY => self::DEFAULT_MAX_RETRY_VALUE,
|
|
self::TTL_RETRY => self::DEFAULT_RETRY_DELAY_VALUE,
|
|
],
|
|
MessagePublisher::CREATE_RECORD_TYPE => [
|
|
'with' => self::WITH_RETRY,
|
|
self::MAX_RETRY => self::DEFAULT_MAX_RETRY_VALUE,
|
|
self::TTL_RETRY => self::DEFAULT_RETRY_DELAY_VALUE,
|
|
],
|
|
MessagePublisher::DELETE_RECORD_TYPE => [
|
|
'with' => self::WITH_NOTHING,
|
|
self::MAX_RETRY => self::DEFAULT_MAX_RETRY_VALUE,
|
|
],
|
|
MessagePublisher::EXPORT_MAIL_TYPE => [
|
|
'with' => self::WITH_RETRY,
|
|
self::MAX_RETRY => self::DEFAULT_MAX_RETRY_VALUE,
|
|
self::TTL_RETRY => self::DEFAULT_RETRY_DELAY_VALUE,
|
|
],
|
|
MessagePublisher::DOWNLOAD_ASYNC_TYPE => [
|
|
'with' => self::WITH_RETRY,
|
|
self::MAX_RETRY => self::DEFAULT_MAX_RETRY_VALUE,
|
|
self::TTL_RETRY => self::DEFAULT_RETRY_DELAY_VALUE,
|
|
],
|
|
MessagePublisher::EXPOSE_UPLOAD_TYPE => [
|
|
'with' => self::WITH_RETRY,
|
|
self::MAX_RETRY => self::DEFAULT_MAX_RETRY_VALUE,
|
|
self::TTL_RETRY => self::DEFAULT_RETRY_DELAY_VALUE,
|
|
],
|
|
MessagePublisher::FTP_TYPE => [
|
|
'with' => self::WITH_RETRY,
|
|
self::MAX_RETRY => self::DEFAULT_MAX_RETRY_VALUE,
|
|
self::TTL_RETRY => 180 * 1000,
|
|
],
|
|
MessagePublisher::MAIN_QUEUE_TYPE => [
|
|
'with' => self::WITH_NOTHING,
|
|
self::MAX_RETRY => self::DEFAULT_MAX_RETRY_VALUE,
|
|
],
|
|
MessagePublisher::POPULATE_INDEX_TYPE => [
|
|
'with' => self::WITH_RETRY,
|
|
self::MAX_RETRY => self::DEFAULT_MAX_RETRY_VALUE,
|
|
self::TTL_RETRY => self::DEFAULT_RETRY_DELAY_VALUE,
|
|
],
|
|
MessagePublisher::PULL_ASSETS_TYPE => [
|
|
'with' => self::WITH_LOOP,
|
|
self::MAX_RETRY => self::DEFAULT_MAX_RETRY_VALUE,
|
|
self::TTL_RETRY => self::DEFAULT_RETRY_DELAY_VALUE,
|
|
],
|
|
MessagePublisher::EDIT_RECORD_TYPE => [
|
|
'with' => self::WITH_RETRY,
|
|
self::MAX_RETRY => self::DEFAULT_MAX_RETRY_VALUE,
|
|
self::TTL_RETRY => self::DEFAULT_RETRY_DELAY_VALUE,
|
|
],
|
|
MessagePublisher::RECORDS_ACTIONS_TYPE => [
|
|
'with' => self::WITH_LOOP,
|
|
self::MAX_RETRY => self::DEFAULT_MAX_RETRY_VALUE,
|
|
self::TTL_RETRY => self::DEFAULT_RETRY_DELAY_VALUE
|
|
],
|
|
MessagePublisher::SUBDEF_CREATION_TYPE => [
|
|
'with' => self::WITH_RETRY | self::WITH_DELAYED,
|
|
self::MAX_RETRY => self::DEFAULT_MAX_RETRY_VALUE,
|
|
self::TTL_RETRY => self::DEFAULT_RETRY_DELAY_VALUE,
|
|
self::TTL_DELAYED => self::DEFAULT_DELAYED_DELAY_VALUE
|
|
],
|
|
MessagePublisher::SUBTITLE_TYPE => [
|
|
'with' => self::WITH_NOTHING,
|
|
self::MAX_RETRY => self::DEFAULT_MAX_RETRY_VALUE,
|
|
],
|
|
MessagePublisher::VALIDATION_REMINDER_TYPE => [
|
|
'with' => self::WITH_LOOP,
|
|
self::MAX_RETRY => self::DEFAULT_MAX_RETRY_VALUE,
|
|
self::TTL_RETRY => 7200 * 1000,
|
|
],
|
|
MessagePublisher::WEBHOOK_TYPE => [
|
|
'with' => self::WITH_RETRY,
|
|
self::MAX_RETRY => self::DEFAULT_MAX_RETRY_VALUE,
|
|
self::TTL_RETRY => self::DEFAULT_RETRY_DELAY_VALUE,
|
|
],
|
|
MessagePublisher::WRITE_METADATAS_TYPE => [
|
|
'with' => self::WITH_RETRY | self::WITH_DELAYED,
|
|
self::MAX_RETRY => self::DEFAULT_MAX_RETRY_VALUE,
|
|
self::TTL_RETRY => self::DEFAULT_RETRY_DELAY_VALUE,
|
|
self::TTL_DELAYED => self::DEFAULT_DELAYED_DELAY_VALUE
|
|
],
|
|
MessagePublisher::SHARE_BASKET_TYPE => [
|
|
'with' => self::WITH_RETRY,
|
|
self::MAX_RETRY => self::DEFAULT_MAX_RETRY_VALUE,
|
|
self::TTL_RETRY => self::DEFAULT_RETRY_DELAY_VALUE,
|
|
],
|
|
];
|
|
|
|
private $queues = []; // filled during construct, from msg list, default values and conf
|
|
|
|
public function __construct(PropertyAccess $conf)
|
|
{
|
|
$defaultConfiguration = [
|
|
'host' => 'localhost',
|
|
'port' => 5672,
|
|
'ssl' => false,
|
|
'user' => 'guest',
|
|
'password' => 'guest',
|
|
'vhost' => '/',
|
|
'heartbeat' => 60,
|
|
];
|
|
|
|
$this->hostConfig = $conf->get(['workers', 'queue', 'worker-queue'], $defaultConfiguration);
|
|
$this->conf = $conf;
|
|
|
|
// fill list of type attributes
|
|
foreach (self::MESSAGES as $name => $attr) {
|
|
$settings = $attr;
|
|
unset($settings['with']);
|
|
$this->queues[$name] = [
|
|
'Name' => $name,
|
|
'QType' => self::BASE_QUEUE,
|
|
'default_settings' => $settings, // to be displayed as placeholder
|
|
'settings' => $settings, // settings belongs only to base_q
|
|
'Exchange' => self::ALCHEMY_EXCHANGE,
|
|
];
|
|
|
|
// a q with retry can fail (after n retry) or loop
|
|
if($attr['with'] & self::WITH_RETRY) {
|
|
$this->queues[$name]['QType'] = self::BASE_QUEUE_WITH_RETRY; // todo : avoid changing qtype ?
|
|
|
|
// declare the retry q, cross-link with base q
|
|
$retry_name = $name . '_retry';
|
|
$this->queues[$name]['RetryQ'] = $retry_name; // link baseq to retryq
|
|
$this->queues[$retry_name] = [
|
|
'Name' => $retry_name,
|
|
'QType' => self::RETRY_QUEUE,
|
|
'Exchange' => self::RETRY_ALCHEMY_EXCHANGE,
|
|
'BaseQ' => $name, // link retryq back to baseq
|
|
];
|
|
|
|
// declare the failed q, cross-link with base q
|
|
$failed_name = $name . '_failed';
|
|
$this->queues[$name]['FailedQ'] = $failed_name; // link baseq to failedq
|
|
$this->queues[$failed_name] = [
|
|
'Name' => $failed_name,
|
|
'QType' => self::FAILED_QUEUE,
|
|
'Exchange' => self::RETRY_ALCHEMY_EXCHANGE,
|
|
'BaseQ' => $name, // link failedq back to baseq
|
|
];
|
|
}
|
|
// a q can be "delayed" to solve "work in progress" lock on records
|
|
if($attr['with'] & self::WITH_DELAYED) {
|
|
// declare the delayed q, cross-link with base q
|
|
$delayed_name = $name . '_delayed';
|
|
$this->queues[$name]['DelayedQ'] = $delayed_name; // link baseq to delayedq
|
|
$this->queues[$delayed_name] = [
|
|
'Name' => $delayed_name,
|
|
'QType' => self::DELAYED_QUEUE,
|
|
'Exchange' => self::RETRY_ALCHEMY_EXCHANGE,
|
|
'BaseQ' => $name, // link delayedq back to baseq
|
|
];
|
|
}
|
|
if($attr['with'] & self::WITH_LOOP) {
|
|
$this->queues[$name]['QType'] = self::BASE_QUEUE_WITH_LOOP; // todo : avoid changing qtype ?
|
|
|
|
// declare the loop q, cross-link with base q
|
|
$loop_name = $name . '_loop';
|
|
$this->queues[$name]['LoopQ'] = $loop_name; // link baseq to loopq
|
|
$this->queues[$loop_name] = [
|
|
'Name' => $loop_name,
|
|
'QType' => self::LOOP_QUEUE,
|
|
'Exchange' => self::RETRY_ALCHEMY_EXCHANGE,
|
|
'BaseQ' => $name, // link loopq back to baseq
|
|
];
|
|
}
|
|
}
|
|
// inject conf values
|
|
foreach($conf->get(['workers', 'queues'], []) as $name => $settings) {
|
|
if(!isset($this->queues[$name])) {
|
|
throw new Exception(sprintf('undefined queue "%s" in conf', $name));
|
|
}
|
|
$this->queues[$name]['settings'] = array_merge($this->queues[$name]['settings'], $settings);
|
|
}
|
|
}
|
|
|
|
public function getBaseQueueNames()
|
|
{
|
|
$keys = array_keys(self::MESSAGES);
|
|
asort($keys);
|
|
return $keys;
|
|
}
|
|
|
|
public function isBaseQueue(string $queueName)
|
|
{
|
|
return array_key_exists($queueName, self::MESSAGES);
|
|
}
|
|
|
|
public function getBaseQueueName(string $baseQueueName)
|
|
{
|
|
$q = $this->getQueue($baseQueueName);
|
|
return $q['Name'];
|
|
}
|
|
|
|
public function hasRetryQueue(string $baseQueueName)
|
|
{
|
|
$q = $this->getQueue($baseQueueName);
|
|
return array_key_exists('RetryQ', $q);
|
|
}
|
|
|
|
public function getRetryQueueName(string $baseQueueName)
|
|
{
|
|
$q = $this->getQueue($baseQueueName, 'RetryQ');
|
|
return $q['Name'];
|
|
}
|
|
|
|
public function getSetting(string $baseQueueName, string $settingName)
|
|
{
|
|
$q = $this->getQueue($baseQueueName);
|
|
return $q['settings'][$settingName];
|
|
}
|
|
|
|
public function getDefaultSetting(string $baseQueueName, string $settingName)
|
|
{
|
|
$q = $this->getQueue($baseQueueName);
|
|
return $q['default_settings'][$settingName];
|
|
}
|
|
|
|
public function hasDelayedQueue(string $baseQueueName)
|
|
{
|
|
$q = $this->getQueue($baseQueueName);
|
|
return array_key_exists('DelayedQ', $q);
|
|
}
|
|
|
|
public function getDelayedQueueName(string $baseQueueName)
|
|
{
|
|
$q = $this->getQueue($baseQueueName, 'DelayedQ');
|
|
return $q['Name'];
|
|
}
|
|
|
|
public function getFailedQueueName(string $baseQueueName)
|
|
{
|
|
$q = $this->getQueue($baseQueueName, 'FailedQ');
|
|
return $q['Name'];
|
|
}
|
|
|
|
public function hasLoopQueue(string $baseQueueName)
|
|
{
|
|
$q = $this->getQueue($baseQueueName);
|
|
return array_key_exists('LoopQ', $q);
|
|
}
|
|
|
|
public function getLoopQueueName(string $baseQueueName)
|
|
{
|
|
$q = $this->getQueue($baseQueueName, 'LoopQ');
|
|
return $q['Name'];
|
|
}
|
|
|
|
public function getExchange(string $queueName)
|
|
{
|
|
$q = $this->getQueue($queueName);
|
|
return $q['Exchange'];
|
|
}
|
|
|
|
private function getQueue(string $queueName, string $subQueueKey = null)
|
|
{
|
|
if(!array_key_exists($queueName, $this->queues)) {
|
|
throw new Exception(sprintf('undefined queue "%s"', $queueName));
|
|
}
|
|
if($subQueueKey && !array_key_exists($subQueueKey, $this->queues[$queueName])) {
|
|
throw new Exception(sprintf('base queue "%s" has no "%s"', $queueName, $subQueueKey));
|
|
}
|
|
return $subQueueKey ? $this->queues[$this->queues[$queueName][$subQueueKey]] : $this->queues[$queueName];
|
|
}
|
|
|
|
public function getConnection()
|
|
{
|
|
if (empty($this->connection)) {
|
|
try {
|
|
$heartbeat = $this->hostConfig['heartbeat'] ?? 60;
|
|
|
|
// if we are in ssl connection type
|
|
if (isset($this->hostConfig['ssl']) && $this->hostConfig['ssl'] === true) {
|
|
$sslOptions = [
|
|
'verify_peer' => true,
|
|
];
|
|
|
|
$this->connection = new AMQPSSLConnection(
|
|
$this->hostConfig['host'],
|
|
$this->hostConfig['port'],
|
|
$this->hostConfig['user'],
|
|
$this->hostConfig['password'],
|
|
$this->hostConfig['vhost'],
|
|
$sslOptions,
|
|
[
|
|
'heartbeat' => $heartbeat,
|
|
]
|
|
);
|
|
} else {
|
|
$this->connection = new AMQPStreamConnection(
|
|
$this->hostConfig['host'],
|
|
$this->hostConfig['port'],
|
|
$this->hostConfig['user'],
|
|
$this->hostConfig['password'],
|
|
$this->hostConfig['vhost'],
|
|
false,
|
|
'AMQPLAIN',
|
|
null,
|
|
'en_US',
|
|
3.0,
|
|
3.0,
|
|
null,
|
|
false,
|
|
$heartbeat
|
|
);
|
|
}
|
|
}
|
|
catch (Exception $e) {
|
|
// no-op
|
|
}
|
|
}
|
|
|
|
return $this->connection;
|
|
}
|
|
|
|
public function getChannel()
|
|
{
|
|
if (empty($this->channel)) {
|
|
$this->getConnection();
|
|
if (!empty($this->connection)) {
|
|
$this->channel = $this->connection->channel();
|
|
|
|
return $this->channel;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
else {
|
|
return $this->channel;
|
|
}
|
|
}
|
|
|
|
public function declareExchange()
|
|
{
|
|
if (isset($this->channel)) {
|
|
$this->channel->exchange_declare(self::ALCHEMY_EXCHANGE, 'direct', false, true, false);
|
|
$this->channel->exchange_declare(self::RETRY_ALCHEMY_EXCHANGE, 'direct', false, true, false);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param $queueName
|
|
* @return AMQPChannel|null
|
|
* @throws Exception
|
|
*/
|
|
public function setQueue($queueName)
|
|
{
|
|
// first send heartbeat
|
|
// catch if connection closed, and get a new one connection
|
|
if (!empty($this->connection)) {
|
|
try {
|
|
$this->connection->checkHeartBeat();
|
|
} catch(\Exception $e) {
|
|
$this->connection = null;
|
|
$this->channel = null;
|
|
$this->getChannel();
|
|
}
|
|
} else {
|
|
$this->connection = null;
|
|
$this->channel = null;
|
|
$this->getChannel();
|
|
}
|
|
|
|
if (!isset($this->channel)) {
|
|
$this->getChannel();
|
|
if (!isset($this->channel)) {
|
|
// can't connect to rabbit
|
|
return null;
|
|
}
|
|
|
|
$this->declareExchange();
|
|
}
|
|
|
|
$queue = $this->queues[$queueName];
|
|
switch($queue['QType']) {
|
|
case self::BASE_QUEUE_WITH_RETRY:
|
|
$this->queue_declare_and_bind($queueName, self::ALCHEMY_EXCHANGE, [
|
|
'x-dead-letter-exchange' => self::RETRY_ALCHEMY_EXCHANGE, // the exchange to which republish a 'dead' message
|
|
'x-dead-letter-routing-key' => $queue['RetryQ'] // the routing key to apply to this 'dead' message
|
|
]);
|
|
$this->setQueue($queue['RetryQ']);
|
|
break;
|
|
case self::BASE_QUEUE_WITH_LOOP:
|
|
$this->queue_declare_and_bind($queueName, self::ALCHEMY_EXCHANGE, [
|
|
'x-dead-letter-exchange' => self::RETRY_ALCHEMY_EXCHANGE, // the exchange to which republish a 'dead' message
|
|
'x-dead-letter-routing-key' => $queue['LoopQ'] // the routing key to apply to this 'dead' message
|
|
]);
|
|
$this->setQueue($queue['LoopQ']);
|
|
break;
|
|
case self::LOOP_QUEUE:
|
|
case self::RETRY_QUEUE:
|
|
$this->queue_declare_and_bind($queueName, self::RETRY_ALCHEMY_EXCHANGE, [
|
|
'x-dead-letter-exchange' => self::ALCHEMY_EXCHANGE,
|
|
'x-dead-letter-routing-key' => $queue['BaseQ'],
|
|
'x-message-ttl' => (int)$this->queues[$queue['BaseQ']]['settings'][self::TTL_RETRY]
|
|
]);
|
|
break;
|
|
case self::DELAYED_QUEUE:
|
|
$this->queue_declare_and_bind($queueName, self::RETRY_ALCHEMY_EXCHANGE, [
|
|
'x-dead-letter-exchange' => self::ALCHEMY_EXCHANGE,
|
|
'x-dead-letter-routing-key' => $queue['BaseQ'],
|
|
'x-message-ttl' => (int)$this->queues[$queue['BaseQ']]['settings'][self::TTL_DELAYED]
|
|
]);
|
|
break;
|
|
case self::FAILED_QUEUE:
|
|
$this->queue_declare_and_bind($queueName, self::RETRY_ALCHEMY_EXCHANGE, [
|
|
'x-message-ttl' => 604800*1000 // message in failed_q to be dead after 7 days by default
|
|
]);
|
|
break;
|
|
case self::BASE_QUEUE:
|
|
$this->queue_declare_and_bind($queueName, self::ALCHEMY_EXCHANGE);
|
|
break;
|
|
default:
|
|
throw new Exception(sprintf('undefined q type "%s', $queueName));
|
|
}
|
|
|
|
return $this->channel;
|
|
}
|
|
|
|
private function queue_declare_and_bind(string $name, string $exchange, array $arguments = null)
|
|
{
|
|
try {
|
|
$this->channel->queue_declare(
|
|
$name,
|
|
false, true, false, false, false,
|
|
$arguments ? new AMQPTable($arguments) : null
|
|
);
|
|
$this->channel->queue_bind($name, $exchange, $name);
|
|
}
|
|
catch (Exception $e) {
|
|
// the q exists and arguments don't match, fallback.
|
|
// Happens when we try to get the number of messages (getQueueStatus)
|
|
// after the settings (e.g. ttl) was changed and the q was not yet re-created
|
|
$this->getConnection();
|
|
if (isset($this->connection)) {
|
|
$this->channel = $this->connection->channel();
|
|
}
|
|
$this->channel->queue_declare($name,true);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* purge some queues, delete related retry-q
|
|
* nb: called by admin/purgeQueuAction, so a q may be __any kind__ - not only base-types !
|
|
*
|
|
* @param array $queueNames
|
|
* @throws Exception
|
|
*/
|
|
public function reinitializeQueue(array $queueNames)
|
|
{
|
|
if (!isset($this->channel)) {
|
|
$this->getChannel();
|
|
$this->declareExchange();
|
|
}
|
|
|
|
foreach ($queueNames as $queueName) {
|
|
// re-inject conf values (some may have changed)
|
|
$settings = $this->conf->get(['workers', 'queues', $queueName], []);
|
|
if($this->isBaseQueue($queueName) && array_key_exists($queueName, $this->queues)) {
|
|
$this->queues[$queueName]['settings'] = array_merge($this->queues[$queueName]['settings'], $settings);
|
|
}
|
|
|
|
if(array_key_exists($queueName, self::MESSAGES)) {
|
|
// base-q
|
|
$this->purgeQueue($queueName);
|
|
|
|
if($this->hasRetryQueue($queueName)) {
|
|
$this->deleteQueue($this->getRetryQueueName($queueName));
|
|
}
|
|
if($this->hasLoopQueue($queueName)) {
|
|
$this->deleteQueue($this->getLoopQueueName($queueName));
|
|
}
|
|
}
|
|
else {
|
|
// retry, delayed, loop, ... q
|
|
$this->deleteQueue($queueName);
|
|
}
|
|
|
|
$this->setQueue($queueName);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* delete a queue, fails silently if the q does not exists
|
|
*
|
|
* @param $queueName
|
|
*/
|
|
public function deleteQueue($queueName)
|
|
{
|
|
if (!isset($this->channel)) {
|
|
$this->getChannel();
|
|
$this->declareExchange();
|
|
}
|
|
try {
|
|
$this->channel->queue_delete($queueName);
|
|
}
|
|
catch(Exception $e) {
|
|
// no-op
|
|
}
|
|
}
|
|
|
|
/**
|
|
* purge a queue, fails silently if the q does not exists
|
|
*
|
|
* @param $queueName
|
|
*/
|
|
public function purgeQueue($queueName)
|
|
{
|
|
if (!isset($this->channel)) {
|
|
$this->getChannel();
|
|
$this->declareExchange();
|
|
}
|
|
try {
|
|
$this->channel->queue_purge($queueName);
|
|
}
|
|
catch(Exception $e) {
|
|
// no-op
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get queueName, messageCount, consumerCount of queues
|
|
* @return array
|
|
* @throws Exception
|
|
*/
|
|
public function getQueuesStatus($hideEmptyQ = true, $consumedQ = true)
|
|
{
|
|
$this->getChannel();
|
|
$queuesStatus = [];
|
|
|
|
foreach($this->queues as $name => $queue) {
|
|
|
|
$this->setQueue($name); // todo : BASE_QUEUE_WITH_RETRY will set both BASE and RETRY Q, so we should skip one of 2
|
|
|
|
$this->getConnection();
|
|
if (isset($this->connection)) {
|
|
$this->channel = $this->connection->channel();
|
|
}
|
|
try {
|
|
list($queueName, $messageCount, $consumerCount) = $this->channel->queue_declare($name, true);
|
|
if ($hideEmptyQ && $messageCount == 0) {
|
|
continue;
|
|
}
|
|
|
|
if ($consumedQ && $consumerCount == 0) {
|
|
continue;
|
|
}
|
|
|
|
$queuesStatus[$queueName] = [
|
|
'queueName' => $queueName,
|
|
'exists' => true,
|
|
'messageCount' => $messageCount,
|
|
'consumerCount' => $consumerCount
|
|
];
|
|
}
|
|
catch (Exception $e) {
|
|
// should not happen since "setQueue()" was called
|
|
$queuesStatus[$name] = [
|
|
'queueName' => $name,
|
|
'exists' => false,
|
|
'messageCount' => -1,
|
|
'consumerCount' => -1
|
|
];
|
|
}
|
|
}
|
|
|
|
ksort($queuesStatus);
|
|
|
|
return $queuesStatus;
|
|
}
|
|
|
|
public function connectionClose()
|
|
{
|
|
$this->channel->close();
|
|
$this->connection->close();
|
|
}
|
|
}
|