· ildyria · Livewire  · 5 min read

Livewire performances problems 📉

A look back on Server-Side rendering performance with Livewire in Lychee v5.

A look back on Server-Side rendering performance with Livewire in Lychee v5.

On December 25th, we released Lychee version 5. This was the first major version bump since April 2020. This new version brings the latest and shiniest part of the laravel ecosystem: Livewire.

What is Livewire?

Livewire aims to bridge the gap between the front-end and the backend, with an ambitious message: “you no longer need to know JS to do front-end reactivity”.
The premise is appealing:

  • no more AJAX requests,
  • no more JS needed to build the DOM (like in Lychee v3 and v4),
  • no more DOM manipulation,
  • no more events to track,
  • only PHP (stronger type garantees),
  • re-using blade templates & Laravel components,
  • in place replacement with dom-diffing…
  • Single Page Application behaviour with url updates.

All this is done with this single library which takes care of keeping a cryptographically authenticated state, provide events annotation hooks on html elements and forward those calls to the Server, executing direclty php methods on the components.

So far soo good. It took me about 1.5 years to re-implement the front-end, added tests. We are ready to ship.

A sad reality: local development vs real life server performance.

When doing local development with Livewire, everything seems fast. The request are instantanuous, the reactivity is palpable. However, as soon as you are deploying on an external server, you are immediately faced with a harsh reality: Latency is a B**tch.

What is happening? Any action with behaviour is trigerring a call to the server. This round-trip instantly kills the fluidness previously seen. Everything is slow, opening menus are taking ages… Switching from one picture to another is terrible.

There is however a solution: AlpineJS. A small library to leaverage local interaction such as hover, opening menus etc to JavaScript and keep the rest with Server Round trip. One could say that Alpine is very close to VueJS in its design, to the point where the naming convension are similar: x-on instead of v-on, x-html instead of v-html, etc…

Using AlpineJS, I rewrote the photo navigation and editing part, I rewrote the layout (justified, masonry etc.) of the pictures in albums. Finally Lychee v5 was getting usable.

A N + 1 query blade for-loop.

We got report from our users that when using a large number of albums and sub-albums, Lychee was getting slower beyond what would be acceptable. We pop back debug mod with the trusted debug bar and have a look at what is going on.

354 SQL queries with 348 duplicates

What do we see? Server takes 2,43 seconds to respond and for 45 sub-albums we have 354 SQL statement executed with 348 of them being duplicates. There is no doubt, we have a N+1 problem.

After a few hours of debugging the culprit was found: the id of the thumb of the parent album [Parent] was being queried for every sub-album [child] in order to check whether the current sub album [child] was used to specify the facade of the album [Parent]. With a bit of caching, this went away quickly.

Similarly, the computation of the parent album [parent] access rights were done for every single [child] instance. This lead to another set of duplicated queries.

After a few updates and iterations, we got the following results.

33 SQL queries with 17 duplicates

33 Queries, with 17 duplicates. Good enough. At least that number is no longer related to the number of Sub-Albums, it is therefore a flat cost. We will bit the bullet for now…

Still slow: Serialization

Still after this being solved, reports kept coming that Lychee was still slow when opening albums with large number of pictures. In order to track down this issue, we can no longer use DebugBar, it is not precise enough. So we turn ourselves to ClockWork, a powerful request analyzer.

Clockwork vue

This is a request opening an album with 700 pictures. No sub-albums, just a collection of photos. Without XDebug enabled (it usually produces a 20x slowdown), it takes 3 seconds for the server to produce the data, and render the page. What is going on?

Scrolling down we see the following waterfal. In Red are the SQL queries, in pruple the rendering of the components. Clockwork serialization

It is obvious that the roadblock is not dues to SQL queries: 2876ms are spent in the app, while 162m are spent waiting for the database. What is happening in that purple part? Simple, data are being serialzied to be sent to the front-end. Do note that we already did optimize our communcation between the server and the front-end:

  • we only send the minimum amount of data to needed to be displayed.
  • we only use the id of the models, all the other information are kept serverside. This has two benefits, it prevents exposing uncontrolled data (e.g. password field not in the $hidden attribute of a model will be displayed by the toArray() which is used by default by Laravel to serialize models. ).
  • Serialization with toArray() breaks in our case due to the recursive nature of the Parent-Child relationship in albums.

As a result, it is pretty clear that at least 75% of those 2876 ms are mostly spent on serialization of data. This is assumption is also quickly verified: by setting LIVEWIRE_ENABLED=false in your .env, you are reverting back to the front-end of Version 4. The results are immediate. The speed kicks in and Lychee feels snappier.

We dig a little more in the serialization problem with Laravel xprof profiler, because maybe there are some quick gains.

Hprof

The most expensive function calls are the transformation of Models from the SQL query row into their respective object. This is not specially surprising. However when looking at the following parts, we notice something suprising.

Hprof1

Hprof2

Hprof3

The Carbon object is initialized every time, and a lot of time is spent in finding on which timezone the server is running…

Conclusion

On this note, it becomes pretty obvious that for Lychee, Livewire and Server Side Rendering are not a good option. We tried to follow the hype and be at the bleeding edge of technology. While those ideas are appealing at first, they became quickly a nightmare to optimize and diagnose.

To know what is coming next, read the our next post here.

Support us!

If you are using Lychee, a small token of gratitude will go a long way. You can support further development of Lychee on GitHub.

Back to Blog

Related Posts

View All Posts »
Bite-size v6: Beta.2 is Live!

Bite-size v6: Beta.2 is Live!

Bite-size v6 is a series of small post showing the progress made on the development of the future version of Lychee.

Bite-size v6: Beta is Live!

Bite-size v6: Beta is Live!

Bite-size v6 is a series of small post showing the progress made on the development of the future version of Lychee.