Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

mathcrln's avatar

Poor performances when migrating from SQLite to Postgresql

I am using Laravel 11 with Inertia.js and React. Until now, as I was prototyping, I was using SQLite in development environment as well as in a test prod environnement I had set up.

Recently attempted to deploy to a real production server based in Switzerland (I'm based in Montréal), with Laravel Forge to provision the server, and Postgresql instead of SQLite (hosted on the same server).

Ever since doing the switch from SQLite to Postgresql, I observe huge performances issues, on every page change, to the point where, when deployed, most pages return 504 timeout. In my local environnement, when connected to the same database, I don't have 504, but the requests are very noticeably slow, taking 10, 40 seconds sometimes.

I considered that the issue might come from latency, but while the exact delay changes, I observed the same behaviour with running the database:

  • On the Forge provisionned server I mentioned and accessing the website hosted on that server (so no network latency)
  • On that same server but connecting with a VPS tunnel on my machine
  • On my local machine (macbook pro M3) with psql
  • On Supabase with a database hosted in Paris (further from me)
  • On Supabase with a database hosted in Canada (closer to me)

I attempted to refactor my code, my migrations, adding indexes, changing types.

Debugbar show relatively efficient requests, I do eager loading when necessary. Most are around 300ms.

When switching back to SQLite, on the exact version of the code, the website is back to being snappy and performant. I know that, SQLite being in memory, it's supposedly faster, but here the performances with posgresql are really abyssimal.

As such, I assume that there might be some framework related overhead of my doing, like a missing constraint, etc, but I can't identify it on my own and I wonder if anyone came across such behaviour?

I am not excluding that the issue might come from somewhere else in my code (controllers, services, API resources, etc), but just that it only appears when i switch from SQLite to Postgresql.

Some additionnal notes:

  • Doing some tasks like seeding the database is very slow when i connect to the forge hosted database (can take up to 6 minutes), while it's rather fast (albeit noticeably slower than sqlite) when on a local database
  • Tinker takes slightly more time to load from scratch and answer queries once open, but it's barely noticeable.

Thank you in advance for your answer!

0 likes
6 replies
LaryAI's avatar
Level 58

When migrating from SQLite to PostgreSQL and experiencing performance issues, there are several areas to investigate and optimize. Here are some steps and considerations to help diagnose and potentially resolve the performance problems:

  1. Database Configuration:

    • Ensure that PostgreSQL is properly configured for your server's resources. The default configuration is often not optimized for production use. Consider using tools like pgtune to generate a configuration file based on your server's specifications.
  2. Indexes:

    • Double-check that all necessary indexes are in place. While you mentioned adding indexes, ensure that they are correctly applied to columns used in WHERE, JOIN, and ORDER BY clauses.
  3. Query Optimization:

    • Use PostgreSQL's EXPLAIN ANALYZE to understand how queries are executed and identify any bottlenecks.
    • Ensure that your queries are optimized and that you are using eager loading effectively to minimize the number of queries.
  4. Connection Pooling:

    • Implement a connection pooler like PgBouncer to manage database connections efficiently. This can help reduce the overhead of establishing new connections.
  5. Network Latency:

    • Although you mentioned testing in various environments, ensure that network latency is not a factor by testing with the database and application on the same local network.
  6. Database Maintenance:

    • Regularly run VACUUM and ANALYZE on your PostgreSQL database to reclaim storage and update statistics for the query planner.
  7. Logging and Monitoring:

    • Enable logging in PostgreSQL to capture slow queries and other performance-related data. This can provide insights into what might be causing delays.
  8. Laravel Configuration:

    • Ensure that your Laravel database configuration is optimized for PostgreSQL. Check settings like DB_CONNECTION, DB_HOST, DB_PORT, DB_DATABASE, DB_USERNAME, and DB_PASSWORD in your .env file.
  9. Hardware Resources:

    • Verify that your server has adequate CPU, memory, and disk I/O resources to handle the load. PostgreSQL can be resource-intensive, especially compared to SQLite.
  10. Data Types and Schema:

    • Review your database schema for any data type mismatches or inefficiencies. PostgreSQL has different data types than SQLite, and using the appropriate types can improve performance.
  11. Laravel Debugging:

    • Use Laravel's debugging tools, like the Debugbar, to profile your application and identify slow parts of your code.

If after these steps the performance issues persist, consider consulting with a database performance expert who can provide more tailored advice based on your specific setup and workload.

martinbean's avatar

@mathcrln You need to do some proper debugging and see where the time in a request is being spent. Just using PostgreSQL shouldn’t cause 10+ second response times.

Work out where the lag is, and you’ll be able to zero in on the problem. Unfortunately people can’t really help with, “My site is slow”-type questions as the reason could be a multitude of possibilities.

mathcrln's avatar

@martinbean Thank you @martinbean! Well, that's what I've been attempting to do, as I tried to demonstrate in my original post. I tried with multiple database providers, in different locations, tried to see where the overhead could come from (like if there was any chance it could come from inertia or the api resources). I even doubled my server resources to see if it could be some form of CPU bottleneck.

It clearly does, to some extent, come from the migration from sqlite to postgresql as even tinker is stalling, when ran on the server.

My current hypothesis is that it comes from something that's barely legal in Postgresql (hence why it doesn't give me any error), but that sqlite is very lenient with, like, a missing unique() on a constrained field or something. I just wondered if anyone ever came across such behaviour.

Either way I thank you for your answer! And I must agree, such questions are hard to answer, but I already spent multiple days debugging and trying multiple scenarios, so I reached for my peers.

martinbean's avatar

@mathcrln You need to measure a single request rather than just swapping things in and out, hoping it’ll magically fix things.

There are many steps between you typing a URL into a browser’s address bar and hitting enter, and then getting a HTML page rendered in your browser. The following things will occur:

  • Address is converted to IP address
  • Request is sent to server at that IP address for handling
  • Server uses web server like Apache or nginx to route the request
  • A script (i.e. Laravel’s front controller) picks up the request for handling
  • Laravel then has various stages it goes through:
    • Framework is booted
    • Global middleware is ran
    • Router tries to route request
    • Route-specific middleware is ran
    • Request is routed to a controller action for handling
    • The controller action that does things, including connecting to database if not already connected
    • Controller action performs business logic
    • Controller action returns response
    • Any middleware handling responses are ran
    • Response reaches your browser

So there are a lot of steps (and probably more I’ve missed given I’m typing this at almost 1 AM local time). You need to measure the time in between these steps for a single request, and see where the latest proportion of the time is spent. When you identify where a request is getting help up, you then have a better idea where the actual problem may lie, and where to focus your efforts.

mathcrln's avatar

@martinbean Thank you for taking the time at 1AM, i appreciate it!

And thank you as well for your insight! I admit that I still lack the tools to test every step of the lifecycle of my requests that you mentioned (if you have any specific tool other than debugbar, POSTGRESQL Analyze, \DB::enableQueryLog()). I'm not saying that Postgresql is the culprit, I'm just saying that it's the one that I did identify.

  • Pages that don't rely on data being fetched from the database load almost instantly (eg contact page, though there is still the session handler being ran).
  • As mentioned, no problem whatsoever is perceived with sqlite
  • Even pages with the barest and most basic controllers/logic run slow
class PageController extends Controller
{

    public function __invoke(Request $request)
    {
        $page = Page::where('slug', $request->slug)->firstOrFail();

        return inertia('Pages/Show', compact('page'));
    }
}

(in web.php)

			Route::get('/{slug}', PageController::class)->name("page.show");

In fact, even a api route that 1) doesn't return a view 2) doesn't have any middleware applied return a 504 Gateway TIme-out

Route::get('/api/programs', [ProgramAPIController::class, 'index']); 
Route::get('/api/programs/{slug}', [ProgramAPIController::class, 'show']);

Beside my idea that it could be due to inefficient db design on my part, it could also be some form of bottleneck caused by php-fpm or misconfiguration with nginx? It's just hard to debug or fix.

mathcrln's avatar

I solved my issue. I made a test with MySQL, which highlighted some issues in a couple migrations files. Postgresql didn't mention anything, but i think it's the reason why the engine was stalling.

  $table->unsignedBigInteger('created_by')->nullable();
  $table->foreign('created_by')->references('id')->on('users')->onDelete('set null');

In my "media" table (used everywhere) I had a "onDelete('set null')" reference on the users table, but the field was not nullable. I had the same issues in a couple of other migration files.

While I will stick with MySQL, performances seem to have greatly improved with postgresql as well, following that change, whether local or remote. I still find it much slower than mysql though, in a noticeable but acceptable way.

Thank you again for your help, @martinbean!

1 like

Please or to participate in this conversation.