Caching liveview requests from first mount

Suppose we have a liveview that renders a lot of assigns. Currently we have duplicated requests to database to fetch data for assigns that gets called in mount. I’d like to avoid having duplicated requests so here are the alternatives:

  1. Add in live.html.leex a check if socket is connected and only then render the page, the problem with this approach is that user has to wait for socket to get connected for any content to appear.
  2. Add default values that get rendered when first mount gets called, the problem in this case is that user will see garbage if socket gets connected slow.
  3. Cache the assigns in an agent on first mount and then restore them when socket is connected.

The last approach seems the most fitting, but I wonder are there any drawbacks, beside having an additional process spawned?

Nothing wrong with it, I’d say.

Maybe this will help you: Caching Phoenix LiveView - YouTube

2 Likes

I’ve listed a few things to consider when trying to solve this with caching in this one:

https://kobrakai.de/kolumne/liveview-double-mount/

5 Likes

This is a common thing that it seems we’re all “solving”, and often with some sort of caching … but caching can be easy to get wrong (too much data being cached, poor key choices, rolling-your-own-vs-using-a-hex-package, …)

I wonder if it would make sense to offer a ‘canonical’ caching mechanism with LiveView to allow a best practice for caching params like this as an option … or would it be near-impossible to create a 90%-useful solution that wouldn’t lead people astray into abusing said caching? what do you think @chrismccord ?

1 Like

It fails on multinode as you could load-balance to a different server.

1 Like

I’ve elaborated on other threads, but in general I would do no extra caching layer for this initial HTTP phase until I had valid need to do so. It’s true you’ll make 2x queries, but you also reduce DB load afterwards, by holding onto state instead of making new stateless requests, fetching current user, current org, prefs, etc for every action the user performs. Also double mount is not happening when using live_redirect, so this is only the initial request for most apps.

Like any decision to opt-in to complexity for optimization’s sake, it should be made on a case by base basis. Most likely, it’s going to be fine as is. Measure and make informed decisions before throwing caching layers on top :slight_smile:

7 Likes

Absolutely. FWIW most of my LiveViews don’t need to worry/care about this (as you noted), but I have a few that have particularly heavy initial load requirements and those have benefited from avoiding the double-mount price.

In those specific cases I would only load the results in the connected mount which solves the problem with minimal work, assuming these pages don’t require SEO

2 Likes

Nah, no SEO (they are all behind authentication), and I have also done “on connect, load”, I even have a macro for this to avoid boilerplate :slight_smile: Various ways to skin this cat.

UX be damned, I’m doing this

HTML

<div :if={@connected}>
  <!-- all the rest of my html -->
</div>
<div :if={not @connected}>
  Loading...
</div>

LV

def mount(params, session, socket) do
  if connected?(socket) do
     socket
     |> assign(:connected, true)
     |> hit_the_database_and_load_everything(params, session)
     |> then(&{:ok, &1})
  else
     socket
     |> assign(:connected, false)
     |> then(&{:ok, &1})
 end
end

:laughing:

3 Likes

Not just UX, SEO too.

1 Like

You are right.

1 Like