vlauciani's avatar

Database: Read & Write Connections. Which server was used?

I was looking a guide for "read and write connections" here:

In the "read" array, I've specified 3 host; running an eloquent query, is there a way to understand (and Log) which server was used?

Thank you

0 likes
4 replies
Braunson's avatar

If your looking locally on how to check, you can use something like Clockwork or Debugbar and I believe may tell you.

Alternatively you can listen and log the QueryExecuted event, see the docs here.

DB::listen(function (QueryExecuted $query) {
        // $query->sql;
        // $query->bindings;
        // $query->time;
        // $query->connectionName;
});
LaryAI's avatar
Level 58

To determine which server was used for a read operation in a Laravel application that is configured with multiple read connections, you can listen for the Illuminate\Database\Events\QueryExecuted event and then log the connection name along with the query details. Here's how you can do it:

First, you need to create a listener for the QueryExecuted event. You can do this by running the following Artisan command:

php artisan make:listener LogDatabaseQuery --event=Illuminate\Database\Events\QueryExecuted

This will create a new listener class in your app/Listeners directory. Open the generated LogDatabaseQuery listener and modify the handle method to log the connection name and the query:

<?php

namespace App\Listeners;

use Illuminate\Database\Events\QueryExecuted;
use Illuminate\Support\Facades\Log;

class LogDatabaseQuery
{
    /**
     * Handle the event.
     *
     * @param  \Illuminate\Database\Events\QueryExecuted  $event
     * @return void
     */
    public function handle(QueryExecuted $event)
    {
        // Get the connection name
        $connectionName = $event->connectionName;

        // Log the connection name and the query
        Log::info("Query executed on connection '{$connectionName}': {$event->sql}", [
            'bindings' => $event->bindings,
            'time' => $event->time,
        ]);
    }
}

Next, you need to register the listener in your EventServiceProvider. Open the app/Providers/EventServiceProvider.php file and add the listener to the $listen array:

<?php

namespace App\Providers;

use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Event;
use Illuminate\Database\Events\QueryExecuted;
use App\Listeners\LogDatabaseQuery;

class EventServiceProvider extends ServiceProvider
{
    protected $listen = [
        QueryExecuted::class => [
            LogDatabaseQuery::class,
        ],
    ];

    // ...
}

Now, whenever a query is executed, Laravel will trigger the QueryExecuted event, and your listener will log the connection name and the query details.

Please note that logging every query can have a performance impact on your application, especially if you have a high number of queries being executed. It's recommended to use this approach only in a development environment or when debugging specific issues.

vlauciani's avatar

The $query->connectionName variable doesn't contain the host used.

I've three hosts for reading:

        'pgsql_dante' => [
            'driver'            => 'pgsql',
            'read' => [
                'username'      => env('DB_USERNAME_RO', 'forge'),
                'password'      => env('DB_PASSWORD_RO', ''),
                'host'          => ['192.168.0.1', '192.168.0.2','192.168.0.3'],
            ],
            'write' => [
                'username'      => env('DB_USERNAME_RW', 'forge'),
                'password'      => env('DB_PASSWORD_RW', ''),
                'host'          => ['192.168.0.1'],
            ],
            'sticky'            => true,
             . . .
        ],

and I'd like to understand, If possible, which one is used when a query is submitted.

TheOneDaveYoung's avatar

@vlauciani any luck with this so far? I am doing something similar but a bit more basic. I am determining if a particular query was sent to my "read" connection or to the "write" connection. I'm not looking at the individual DB server the request was sent to.

How I've done this is is by setting up a global DB listener in the AppServiceProvider. Then in that I compare the read PDO from the query's connection to the write PDO from the global DB provider. If they are the same then I know that the query was executed by the write connection and I do "stuff" with that knowledge.

You may be able to glean the specific server that executed your query by looking at the connection attributes, specifically PDO::ATTR_CONNECTION_STATUS and/or PDO::ATTR_SERVER_INFO. Something like:

DB::listen(function ($query) {
    Log::info(
        "Query executed",
        [
            'query' => $query->sql,
            'connection' => $query->connectionName,
            'status' => $query->connection->getAttribute(PDO::ATTR_CONNECTION_STATUS),
            'info' => $query->connection->getAttribute(PDO::ATTR_SERVER_INFO),
        ]
    );
});

Please or to participate in this conversation.