query function invalidates the cache every 10 clicks
I was wondering why the Suspense is triggered once in a while, then I realized query is not caching the result long enough. As you can see in this example, once every 10 click's the fetch function is hit again. The question is why query invalidates the cache basically rendering it useless.
Here's the playground: https://playground.solidjs.com/anonymous/40fc1a3b-842a-4a02-8543-ebf113f9e1da
Solid Playground
Quickly discover what the solid compiler will generate from your JSX template
30 Replies
query
's cache is short lived as it's only designed for caching preloaded data. it's not designed as a long-lived cacheoh ok thank you. Maybe mention that very important detail in the docs, and suggest an alternative, I used to use "cache" but the docs already says the use query instead. So what is the purpose of revalidate then if no long lived cache exists.
if you want to prevent the suspense from reappearing either wrap the signal update in
startTransition
or use aa.latest
can you elaborate what is happening with your suggestion because I don't understand.
ok technically the cache is long-lived since the data doesn't just disappear, but it's considered stale after a very short time. revalidate is so that you can refetch the data manually, since
query
doesn't automatically refetch when it becomes stale
aa.latest
will only suspend on first load, it'll use the previous value rather than re-suspending on refetch.
startTransition
tells Suspense
to display previous content instead of the fallbackok thank you very much.
Compare it to this one:
https://playground.solidjs.com/anonymous/a8bf69e5-f3dd-4755-843a-7bd421074925
Solid Playground
Quickly discover what the solid compiler will generate from your JSX template
Your example has two cache values:
-
-
query(id=1)
-
query(id=2)
Those cache values have reference counts and the time they were initially loaded. If the first createAsync
connects to the query
before the 5 second expiry then it will not rerun (de-duping for preloads).
And while the reference count to query(id)
remains above 0 additional createAsync
connects will not rerun either.
The moment the reference count goes to 0 and you are outside the intial 5 sec window it's game over- the next connect by a createAsync
will cause a rerun.
In your example you constantly swap between either value, letting the reference count of the other drop to 0. So once you are outside of the 5 second window it will invariably rerun.
In my example I make sure the reference count never goes below 1 so it never reruns.
The idea is that as long as you are holding a reference to that query
value, your application is actively managing the value, so it is responsible for revalidating the query
whenever an action modifies the remote value.reload - SolidDocs
Documentation for SolidJS, the signals-powered UI framework
action - SolidDocs
Documentation for SolidJS, the signals-powered UI framework
Accesses to
query
automatically starts a transition if it reruns. At least that is how it worked in past.
Ok, it seems that transitions are started on revalidation and on navigation.
https://playground.solidjs.com/anonymous/de1ecf9e-b57b-42cc-b3c0-39e7b6927ad6Solid Playground
Quickly discover what the solid compiler will generate from your JSX template
ok technically the cache is long-lived since the data doesn't just disappear, but it's considered stale after a very short time. revalidate is so that you can refetch the data manually, since query
doesn't automatically refetch when it becomes stale
So you are saying that revalidate revalidates the 'data in the cache'?
And if so, does it still only last a very short time?Yeah, revalidate says 'mark this data as needing a refetch', and then any
createAsync
that consumes the query
will re-run and cause new data to be fetched. If there is no createAsync
consuming it then the re-run won't happen until there is one.
The data is considered fresh only for a short time yeahThe data is considered fresh only for a short time yeahThere really is no concept of “fresh”/“stale” with
query
; using these terms sets the wrong expectations with people.
1. When an unreferenced query
is referenced it will run.
2. When a referenced query
gets additional references it will not run (for fetch de-duping).
3. Loading of a query (e.g. preload
) is followed by a 5 second grace period during which referencing the unreferenced query
will not re-run it again.When an unreferenced query is referenced it will run.ah is that the case?
As far as I have been able to determine by reading the
query.ts
code and verifying with various experiments, yes.From what i can tell if a query goes from 0 to 1 references it doesn't automatically mean it'll refetch, the data is still considered 'fresh' for a short period and then when it's stale it'll refetch when going from 0 to 1 references
https://playground.solidjs.com/anonymous/f33609f7-4ebf-44c2-a8e1-494c29f2abad
Solid Playground
Quickly discover what the solid compiler will generate from your JSX template
I don't think the cache changes when it's executing in
createAsync
vs preload
the data is still considered 'fresh' for a short periodI'm not denying the behaviour that you are observing, I'm challenging the interpretation. It's too easy to rationalize the “5 seconds” as some “freshness” feature. It's just an initial window to give the de-duping behaviour some time to get going. By design what is preventing
queries
from being reloaded is that they are still being referenced; while there is at least one single reference that behaviour can go on indefinitely.
Not re-running isn't linked to “freshness” but fetch de-duping.
I don't think the cache changes when it's executing inYou are correct. When acreateAsync
vspreload
query
loads it retains it's loading time no matter how the loading was initiated.
However the intent behind the 5 second
limit is so that pre-loading can get an early start on the fetch before the query
references of the page being wired up on the client side start coming in.
Could that 5 second
window prevent re-runs in some other edge cases? Sure. But those edge case aren't why that limit exists.Ahh that makes sense, focusing on the intent instead of the behaviour
So you are saying that revalidate revalidates the 'data in the cache'?Revalidating is the client-side logic telling the client side router that it just did something on the server that invalidates the associated client side
query
values.
That said the example given under revalidate
is bogus:
revalidate
is typically used outside of actions
. In that particular example revalidate
duplicates re-running of the query
:
1. By default any action
will revalidate all actively referenced query
s on the page (i.e. they will all re-run).
2. If you want to narrow which query
s run you have to use reload
.
2b. If you are returning a result from the action
use the options on json
. This is with the understanding that you can only obtain that result via the result
property from useSubmission.
3. The moment you narrow the revalidation keys
, you opt out of single-flight
(i.e. single flight wraps all the values for a route reload; unless something has changed recently).revalidate - SolidDocs
Documentation for SolidJS, the signals-powered UI framework
I'll need more time to digest what you guys just said but I did notice in the code that there are two timeouts that may be related to the discussion:
Do you know what the difference is?
query.ts
PRELOAD_TIMEOUT
is the amount of time data is kept around for preload use, CACHE_TIMEOUT
is a minimum time to wipe unused cached data - solid router does this in the backgroundSo, that sounds like?
1. After
PRELOAD_TIMEOUT
a hover over a link won't have a cache available
2. The data stays in the cach until CACHE_TIMEOUT
, and then
- a) the cache is empty?
- b) the cache no longer exists?
I suspect the answer to this is above but I didn't get it: How can a user or Solid itself use the data during the period between PRELOAD_TIMEOUT
and CACHE_TIMEOUT
?
Or perhaps CACHE_TIMEOUT
is just a way of timing a clean-up.I don't understand what you guys are talking about. Also your example is acting weird, not sure how it is supposed to work, but. Sometimes the
Result: time
value updates sometimes it doesn't update, each time I reload the example. and it never hit's the database again.How can a user or Solid itself use the data during the period between PRELOAD_TIMEOUT and CACHE_TIMEOUT?You're looking at it from a(n expiry-time based) caching perspective. Get that out of your mind.
query
is a fetch de-duping mechanism.
While at least one createAsync
is referencing the value additional references from other createAsync
s will not cause a refetch.
That can go on indefinitely, there is no time limit.
The moment no createAsync
s are referencing that query
, the value is useless.
PRELOAD_TIMEOUT is just an tiny initial window where the value is protected from reloads while the are no createAsync
references.
CACHE_TIMEOUT is the age when the value gets purged entirely because nobody has a reference to it.
---
A fetch result held by a query
wrapper is considered valid as long as the application holds at least one (createAsync
) reference to it.
Once the last reference to the fetch result has been dropped, the result is considered invalid; i.e. the fetch result can disposed of and for the next , “first” new reference a query
re-run is required.it doesn't update, each time I reload the example. and it never hit's the database againThat is exactly the point. This one flips from
id
1 to 2 and back
but these:
are keeping a permanent reference to either result.
This means that as far as query
(a fetch de-duping mechanism) is concerned a refetch is never necessary. If you need to refetch, you use an action
(which by default reloads the route data) or you revalidate(callDb.keyFor(id))
.
https://playground.solidjs.com/anonymous/de1ecf9e-b57b-42cc-b3c0-39e7b6927ad6
https://playground.solidjs.com/anonymous/ecfc76e8-3183-4298-8507-7aaae485025aaction - SolidDocs
Documentation for SolidJS, the signals-powered UI framework
Solid Playground
Quickly discover what the solid compiler will generate from your JSX template
Solid Playground
Quickly discover what the solid compiler will generate from your JSX template
But I don't have the list of id's 1 and 2 before hand to hardcode them like that. I don't see how this is useful to me. I just copied the example code from the real-world project. If you have a recent code repository on how to work out a real world example that would be great. https://github.com/solidjs/solid-realworld
GitHub
GitHub - solidjs/solid-realworld: A Solid Implementation of the Rea...
A Solid Implementation of the Realworld Example App - solidjs/solid-realworld
The question is why query invalidates the cache basically rendering it useless.Your starting premise was incorrect. -
query
is not a caching mechanism; it exists to de-duplicate fetches.
- Nothing got invalidated. Once your application de-references a fetch result, it is immediately considered “garbage”. Similarly while your application maintains a reference to the fetch result that fetch will not be repeated (indefinitely) even if it is referenced again.
Your example de-references the fetch result by switching from ID 1 to 2 and back—so each time it performs a fresh fetch. It is behaving as intended.
If you click it fast enough you run into an edge case. A recent query value has a 5 second window of “protection” from reloading. That time period has nothing to do with caching but it exists to enable route preloading.
If you spin up a fresh “basic” SolidStart template and replace src/routes/about.tsx
with this:
https://discord.com/channels/722131463138705510/910635844119982080/1349086371536572437
you can see how preloading works:
- Load the project on the /
page
- Hover over the link for the /about
page. The preload will fire and the query
will perform the fetch—exactly once; even though the hover event will fire multiple times.
- Finally click the /about
link. The app will navigate to the page and the createAsync
will finally reference the query
—and again there isn't another fetch because the fetch result is protected by that “5 second” window.
The above describes why the “5 second” reload protection window exists. It has nothing to do with “caching”, everything with preloads working in combination with fetch de-duplication.I completely understand your very detailed explanation. I think I have to keep a reference to the fetch results somewhere basically invent my own caching mechanism. Although It's not a main concern to me right now, since my app is all working local first.
If you are concerned about caching, people are usually happy with solid-query. On the other hand people who have worked with React Query often found that
query
's reference counting scheme was good enough but I don't think any of them were “local first”. Using dexie, dexie is your cache; what you are looking for is a sync engine.Oops. I followed bad etiquette and asked a question on someone else's question. It was a good discussion, thanks!