Database Caching Behavior
We're going to explore the database examples with dynamic data and caching. We have already installed better-sqlite3
and have a SQLite database called test.db
. Let's dive in and see how it's set up.
Inside our SQLite database, we have a table called counts
. When we run a SELECT
query on it, we can see that it currently has a count of 49. Our goal is to increment this count.
Creating an Uncached Version
We'll start by creating an uncached version called db-dynamic
. Let's take a look at what it's doing:
async function getCount() {
const out = (await db.prepare("SELECT * FROM counts LIMIT 1").all()) as {
count: number;
}[];
return out[0]?.count;
}
async function setCount(count: number) {
await db.prepare("DELETE FROM counts").run();
await db.prepare("INSERT INTO counts (count) VALUES (?)").run(count);
}
async function Counter() {
return <div className="text-white text-2xl">Count: {await getCount()}</div>;
}
In this file, we have a function called getCount
that uses better-sqlite3
to make a request to the database and retrieve the count. We also have another function called setCount
that allows us to delete all the rows in the counts
table and insert a new count.
We've also created a React server function called counter
that awaits the result of getCount
and returns it.
Finally, we have a page route for DatabaseDynamic
and its server function increment
. We're using a revalidate path here just to ensure that the page updates when we click the button. However, it's important to note that we're not using any caching in this example since it's all uncached by default.
export default function DatabaseDynamic() {
async function increment() {
"use server";
await setCount((await getCount()) + 1);
revalidatePath("/file-counter-dynamic");
}
return (
<div className="flex gap-5">
<Counter />
<IncrementButton increment={increment} />
</div>
);
}
Our goal now is to achieve the same functionality but with dynamicIO
. To do so, the only thing we need to change is going from revalidatePath
to expirePath
. Now, you might end up in a case where you actually need to wrap this in a suspense. As we saw before with the date stuff, that's really easy to do.
Cache with a Tag
Now, let's try a cache version of it. We'll go over to dvb-cached-tag
. In this implementation, we'll do an on-demand invalidation because we're going to cache the count with a tag.
The goal here is to cache the count coming from the database and then invalidate on-demand with a tag. To achieve this, follow these steps:
-
Change the
revalidateTag
toexpireTag
. -
Remove the cache implementation, and go back down to just an async function called
getCount
. -
Add a
cacheTag
and use the"use cache"
with the cache tag. In this example, the cache tag iscountDB
.
async function getCount() {
"use cache";
cacheTag("count-db");
const out = (await db.prepare("SELECT * FROM counts LIMIT 1").all()) as {
count: number;
}[];
return out[0]?.count;
}
Now, when we go back to the example, we can see that the count is displayed and works as expected. When we hit refresh, the count comes from the cache.
Caching Database
To cache a database, you have a couple of options:
- Cache life: Use this method if you want to expire your cache based on a specific time.
- Expire path: Use this method if you prefer to expire your cache by path.
Next, we're going to take a look at how to handle caching when it comes to data coming in off of a local file.