Remember several common Laravel errors

I have used the Laravel framework for a period of time. During this period, I encountered many problems. Some debugging is really not easy. This paper selects several. If I can make you take fewer detours, I won't write in vain.

Error: "Can't swap PDO instance while within transaction"

By querying the Laravel source code, you can confirm that the exception is thrown in the setPdo method:

<?php

public function setPdo($pdo)
{
    if ($this->transactions >= 1) {
        throw new RuntimeException("
            Can't swap PDO instance while within transaction.
        ");
    }

    $this->pdo = $pdo;

    return $this;
}

?>

Literally, this error occurs because the database connection is switched when the transaction is turned on. However, sometimes this error may occur even if the code does not explicitly switch database connections. For example, when executing a query error, the system will determine whether the problem is caused by loss of connections through the tryAgainIfCausedByLostConnection method. If so, the system will be reconnected through the reconnect method. When reconnecting, the system will perform some cleaning work through the disconnect method, in which the setPdo method is called.

After clarifying the causes and consequences, you will naturally know how to solve the problem: check the network situation and confirm the reason for the loss of database connection. This may be caused by a problem with a device or an improper timeout setting. A relatively dirty processing method is to execute the DB::reconnect() method to reconnect the database before querying.

Error: "cannot delete job < ID >: not_found"

In fact, this problem has nothing to do with Laravel, but is caused by the queue service Beanstalk.

Beanstalk

To solve this problem, we need to understand the life cycle of a message: when a message is put into the queue, it enters the READY state. At the same time, It is associated with a TTR (time to run) timer, indicating the allowed running time of this message. When this message is consumed, it enters the "RESERVED" state. After consumption, the message will be deleted. If the consumption time is too long, which is longer than TTR, the system will think that the consumer has hung up, and then return the message from the "RESERVED" state to the READY state and give it to another user Consumer reprocessing. Therefore, the same message may be processed by multiple consumers. The first processed consumer can delete the message normally, while the other consumers will report an error that cannot be deleted when deleting the message.

The solution is very simple. First, you need to ensure that the TTR setting cannot be too small; Second, Beanstalk actually provides a special touch Command to solve the problem of long execution time. In addition, sometimes we may need to lock at the application level to avoid the situation that the same message is processed by multiple consumers at the same time.

Error: "No query results for model"

On the premise that Laravel read / write separation is activated, consumers may receive similar errors when processing messages. A queue command with potential problems is roughly as follows:

<?php

class Foo extends Command implements SelfHandling, ShouldBeQueued
{
    use InteractsWithQueue, SerializesModels;

    protected $bar;

    public function __construct($id)
    {
        $this->bar = Bar::find($id);
    }

    public function handle()
    {
        // $this->bar
    }
}

?>

Obviously, when Laravel read / write separation is enabled, due to the master-slave delay, find may not be able to query the corresponding data. Once we analyze it here, we may change the writing method to the following:

<?php

class Foo extends Command implements SelfHandling, ShouldBeQueued
{
    use InteractsWithQueue, SerializesModels;

    protected $bar;

    public function __construct($id)
    {
        $this->bar = Bar::onWriteConnection()->find($id);
    }

    public function handle()
    {
        // $this->bar
    }
}

?>

That is, the query is fixed on the primary server through Laravel's # onWriteConnection method, but it is actually invalid. The crux of the problem is that during deserialization, the system will call findOrFail once from the server.

<?php

protected function getRestoredPropertyValue($value)
{
    return $value instanceof ModelIdentifier
        ? (new $value->class)->findOrFail($value->id) : $value;
}

?>

Because we cannot HACK into the framework, onWriteConnection is meaningless. In fact, to look at the problem from another angle, just ensure that database objects are not used as attributes during serialization:

<?php

class Foo extends Command implements SelfHandling, ShouldBeQueued
{
    use InteractsWithQueue, SerializesModels;

    protected $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

    public function handle()
    {
        $bar = Bar::onWriteConnection()->find($this->id);
    }
}

?>

The above are some representative error reports I encountered. Welcome to communicate with you.

Added by Dustin013 on Tue, 14 Dec 2021 02:44:51 +0200