Error 'Has not wait the lock' using Yii2 Queue extension and Postgres 10.4

Viewed 1489

I've developed a project in yii2 that uses the yiisoft / yii2-queue extension. This extension stores orders to export very large CSVs. The exported CSVs work correctly but from time to time it throws the exception of the image:https://i.imgur.com/nd4zRrI.png

I think it may be because of the postgres version I've working with: PostgreSQL 10.4 (Debian 10.4-2.pgdg90 + 1) on x86_64-pc-linux-gnu, compiled by gcc (Debian 6.3.0-18 + deb9u1) 6.3.0 20170516 , 64-bit

I have read that for version 9.4 it works. The configuration of the extension that I have is the following:

'components' => [
    'queue' => [
        'class' => \yii\queue\db\Queue::class,
        'tableName' => '{{%queue}}', // Table name
        'channel' => 'default', // Queue channel key
        'db' => require(__DIR__ . '/db.php'),
        'mutex' => [
            'class' => \yii\mutex\PgsqlMutex::class,
            'db' => require(__DIR__ . '/db.php'),
        ],
        'mutexTimeout' => 0,
    ],
]

Does anyone know why this error occurs and how to fix it?

2 Answers
'mutexTimeout' => 0,

This is probably the reason. It means that queue will try acquire mutex once, and if it fails (which may be quite likely on busy queue) it will throw this exception.

I added support for timeout in PgsqlMutex, but there was no release with this feature yet. I suggest to use master branch of yiisoft/yii2 package and set mutexTimeout to non-zero value.

Fix it by change two methods in vendor/yiisoft/yii2-queue/src/drivers/db/Queue.php - https://github.com/yiisoft/yii2-queue/pull/362/files Code:

/**
 * @param array $payload
 */
protected function release($payload)
{
    $mutex = $this->mutex->acquire(__CLASS__ . $this->channel, $this->mutexTimeout);

    try {
        if ($this->deleteReleased) {
            $this->db->createCommand()->delete(
                $this->tableName,
                ['id' => $payload['id']]
            )->execute();
        } else {
            $this->db->createCommand()->update(
                $this->tableName,
                ['done_at' => time()],
                ['id' => $payload['id']]
            )->execute();
        }
    } catch (Exception $e) {
        \Yii::error($e->getMessage());
    } finally {
        if ($mutex) {
            $this->mutex->release(__CLASS__ . $this->channel);
        } else {
            \Yii::warning('Queue->release() Has not waited the lock');
        }
    }
}

/**
 * Moves expired messages into waiting list.
 */
private function moveExpired()
{
    if ($this->reserveTime !== time()) {
        $this->reserveTime = time();
        $this->db->createCommand()->update(
            $this->tableName,
            ['reserved_at' => null],
            '[[reserved_at]] < :time - [[ttr]] and [[reserved_at]] is not null and [[done_at]] is null',
            [':time' => $this->reserveTime]
        )->execute();
    }
}
Related