juanparati/laravel-timeout

Set a timeout to your queries.

0.8 2025-06-17 12:58 UTC

This package is auto-updated.

Last update: 2025-06-17 13:54:35 UTC


README

example workflow

Laravel Query Timeout ⏰

A Laravel database extension that implements query timeouts at the database level, helping you implement the circuit breaker pattern.

Note: This library only supports MariaDB and MySQL databases.

How it works.

Use the \DB::timeout method to set a maximum execution time for your queries:

\DB::timeout(
    3                                     , // Interrupt if a query takes more than 3 seconds
    fn() => \DB::select('SELECT SLEEP(4)'), // Your query comes here
    'myconnection'                          // Keep null for the default connection
);

In the previous example if the query exceeds the specified timeout (3 seconds), it will be terminated and throw a \Juanparati\LaravelTimeout\QueryTimeoutException.

How it works under the hood

Instead of using co-routines or parallel execution monitoring, this library leverages native database features:

The timeout mechanism works by:

  1. Setting the timeout value for the database session
  2. Executing the query
  3. Restoring the original timeout value
╔════════════════════════════════════════════════════════════════════════════════════════════════════╗
║ Example flow for MariaDB                                                                           ║
║                                                                                                    ║
║     ┌─────────────────────────────────────────┐                                                    ║
║   1.│SET @@SESSION.max_statement_time=3;      │ ◀─── Set the desired maximum time for the session  ║
║     └─────────────────────────────────────────┘                                                    ║
║     ┌─────────────────────────────────────────┐                                                    ║
║   2.│SELECT * FROM USERS;                     │ ◀─── Execute the query                             ║
║     └─────────────────────────────────────────┘                                                    ║
║     ┌─────────────────────────────────────────┐                                                    ║
║   3.│SET @@SESSION.max_statement_time=0;      │ ◀─── Restore the original session maximum time     ║
║     └─────────────────────────────────────────┘                                                    ║
╚════════════════════════════════════════════════════════════════════════════════════════════════════╝

Limitations

MySQL-specific

  • Only "select" queries are timed out in MySQL.
  • Unfortunately, MySQL kills the query silently without raising any error and always returns an empty result, so the way that laravel-timeout determines when a query was in reality timed out it's measuring the execution time and creating artificially an exception, so I recommend run only one single query inside the closure when MySQL is used.

MariaDB-specific

  • Old MariaDB embedded servers may not work properly.
  • COMMIT statements don't timeout in Galera clusters.

General Limitations:

  • May be unreliable with persistent connections or connection pools in distributed environments

Best Practices

  1. Keep application logic outside the closure.

✅ Recommended:

$users = null;

\DB::timeout(
    5, 
    fn() => $users = User::where('name', 'like', 'john%')->get()   
);

foreach ($users as $user) {
    if ($user->password_expiration > now()) {
        ... // Your application logic
    }
}

❌ Not recommended:

\DB::timeout(
    5, 
    function() {
        $users = User::where('name', 'like', 'john%')->get()
        
        foreach ($users as $user) {
            if ($user->password_expiration > now()) {
                ... // Your application logic
            }
        }
    }   
);
  1. In MySQL, try to use only one query per closure (Be cautious with ORM operations that might trigger multiple queries).

Configuration

Publish configuration (Optional)

artisan vendor:publish --tag="timeout"