Fixed resetting page cache when some special collections are changed
This commit is contained in:
parent
90266abc91
commit
6b797a42f8
|
@ -26,6 +26,25 @@ export class PageCache {
|
||||||
private readonly contextCache: ContextCache
|
private readonly contextCache: ContextCache
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
get(url: string): Response | undefined {
|
||||||
|
if (import.meta.env.PAGE_CACHING !== "true") return;
|
||||||
|
const cachedPage = this.responseCache.get(url);
|
||||||
|
if (cachedPage) {
|
||||||
|
this.logger.log("Retrieved cached page for", url);
|
||||||
|
return cachedPage?.clone();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
set(url: string, response: Response) {
|
||||||
|
if (import.meta.env.PAGE_CACHING !== "true") return;
|
||||||
|
this.responseCache.set(url, response.clone());
|
||||||
|
this.logger.log("Cached response for", url);
|
||||||
|
if (this.initialized) {
|
||||||
|
this.scheduleSave();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async init() {
|
async init() {
|
||||||
if (this.initialized) return;
|
if (this.initialized) return;
|
||||||
|
|
||||||
|
@ -39,10 +58,7 @@ export class PageCache {
|
||||||
private async precache() {
|
private async precache() {
|
||||||
if (import.meta.env.DATA_CACHING !== "true") return;
|
if (import.meta.env.DATA_CACHING !== "true") return;
|
||||||
|
|
||||||
// Get all pages urls from CMS
|
const allPageUrls = await this.getAllUrls();
|
||||||
const allDocs = (await this.uncachedPayload.getAll()).data;
|
|
||||||
const allPageUrls = allDocs.flatMap((doc) => this.getUrlFromEndpointChange(doc));
|
|
||||||
// TODO: Add static pages likes "/" and "/settings"
|
|
||||||
|
|
||||||
// Load cache from disk if available
|
// Load cache from disk if available
|
||||||
if (existsSync(ON_DISK_RESPONSE_CACHE_FILE)) {
|
if (existsSync(ON_DISK_RESPONSE_CACHE_FILE)) {
|
||||||
|
@ -56,7 +72,7 @@ export class PageCache {
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Do not include cache where the key is no longer in the CMS
|
// Do not include cache where the key is no longer in the CMS
|
||||||
deserializedData = deserializedData.filter(([key]) => allPageUrls.includes(key));
|
deserializedData = deserializedData.filter(([key]) => allPageUrls.has(key));
|
||||||
|
|
||||||
this.responseCache = new Map(deserializedData);
|
this.responseCache = new Map(deserializedData);
|
||||||
}
|
}
|
||||||
|
@ -80,25 +96,98 @@ export class PageCache {
|
||||||
this.logger.log("Precaching completed!", this.responseCache.size, "responses cached");
|
this.logger.log("Precaching completed!", this.responseCache.size, "responses cached");
|
||||||
}
|
}
|
||||||
|
|
||||||
get(url: string): Response | undefined {
|
async invalidate(changes: EndpointChange[]) {
|
||||||
if (import.meta.env.PAGE_CACHING !== "true") return;
|
if (import.meta.env.PAGE_CACHING !== "true") return;
|
||||||
const cachedPage = this.responseCache.get(url);
|
|
||||||
if (cachedPage) {
|
if (
|
||||||
this.logger.log("Retrieved cached page for", url);
|
changes.some(({ type }) =>
|
||||||
return cachedPage?.clone();
|
[
|
||||||
}
|
SDKEndpointNames.getWebsiteConfig,
|
||||||
|
SDKEndpointNames.getLanguages,
|
||||||
|
SDKEndpointNames.getCurrencies,
|
||||||
|
SDKEndpointNames.getWordings,
|
||||||
|
].includes(type)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
await this.resetAllUrls();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
set(url: string, response: Response) {
|
const pagesToInvalidate = new Set<string>(
|
||||||
if (import.meta.env.PAGE_CACHING !== "true") return;
|
changes.flatMap((change) => this.getUrlFromEndpointChange(change))
|
||||||
this.responseCache.set(url, response.clone());
|
);
|
||||||
this.logger.log("Cached response for", url);
|
|
||||||
|
for (const url of pagesToInvalidate) {
|
||||||
|
this.responseCache.delete(url);
|
||||||
|
this.logger.log("Invalidated cache for", url);
|
||||||
|
try {
|
||||||
|
await fetch(`http://${import.meta.env.ASTRO_HOST}:${import.meta.env.ASTRO_PORT}${url}`);
|
||||||
|
} catch (e) {
|
||||||
|
this.logger.log("Revalidation fails for", url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.logger.log("There are currently", this.responseCache.size, "responses in cache.");
|
||||||
if (this.initialized) {
|
if (this.initialized) {
|
||||||
this.scheduleSave();
|
this.scheduleSave();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async resetAllUrls() {
|
||||||
|
const allUrls = await this.getAllUrls();
|
||||||
|
this.responseCache.clear();
|
||||||
|
|
||||||
|
for (const url of allUrls) {
|
||||||
|
try {
|
||||||
|
await fetch(`http://${import.meta.env.ASTRO_HOST}:${import.meta.env.ASTRO_PORT}${url}`);
|
||||||
|
} catch (e) {
|
||||||
|
this.logger.warn("Reset failed for page", url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.initialized) {
|
||||||
|
this.scheduleSave();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private scheduleSave() {
|
||||||
|
if (this.scheduleSaveTimeout) {
|
||||||
|
clearTimeout(this.scheduleSaveTimeout);
|
||||||
|
}
|
||||||
|
this.scheduleSaveTimeout = setTimeout(() => {
|
||||||
|
this.save();
|
||||||
|
}, 10_000);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async save() {
|
||||||
|
if (!existsSync(ON_DISK_ROOT)) {
|
||||||
|
await mkdir(ON_DISK_ROOT, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
const serializedResponses = await Promise.all(
|
||||||
|
[...this.responseCache].map(async ([key, value]) => [key, await serializeResponse(value)])
|
||||||
|
);
|
||||||
|
const serializedResponseCache = JSON.stringify(serializedResponses);
|
||||||
|
await writeFile(ON_DISK_RESPONSE_CACHE_FILE, serializedResponseCache, {
|
||||||
|
encoding: "utf-8",
|
||||||
|
});
|
||||||
|
this.logger.log("Saved", ON_DISK_RESPONSE_CACHE_FILE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
private getLocalizedUrlsFromUrl = (urls: string[]) => {
|
||||||
|
return urls.flatMap((url) => this.contextCache.locales.map((id) => `/${id}${url}`));
|
||||||
|
};
|
||||||
|
|
||||||
|
private async getAllUrls() {
|
||||||
|
const allChanges = (await this.uncachedPayload.getAll()).data;
|
||||||
|
return new Set<string>([
|
||||||
|
...this.getLocalizedUrlsFromUrl(["", "/settings", "/search"]),
|
||||||
|
...allChanges.flatMap((change) => this.getUrlFromEndpointChange(change)),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
private getUrlFromEndpointChange(change: EndpointChange): string[] {
|
private getUrlFromEndpointChange(change: EndpointChange): string[] {
|
||||||
const getUnlocalizedUrl = (): string[] => {
|
const getUnlocalizedUrl = (): string[] => {
|
||||||
switch (change.type) {
|
switch (change.type) {
|
||||||
|
@ -142,65 +231,11 @@ export class PageCache {
|
||||||
case SDKEndpointNames.getChronologyEventByID:
|
case SDKEndpointNames.getChronologyEventByID:
|
||||||
return [`/timeline`];
|
return [`/timeline`];
|
||||||
|
|
||||||
case SDKEndpointNames.getWebsiteConfig:
|
|
||||||
case SDKEndpointNames.getLanguages:
|
|
||||||
case SDKEndpointNames.getCurrencies:
|
|
||||||
case SDKEndpointNames.getWordings:
|
|
||||||
return [...this.responseCache.keys()];
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return getUnlocalizedUrl().flatMap((url) =>
|
return this.getLocalizedUrlsFromUrl(getUnlocalizedUrl());
|
||||||
this.contextCache.locales.map((id) => `/${id}${url}`)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async invalidate(changes: EndpointChange[]) {
|
|
||||||
if (import.meta.env.PAGE_CACHING !== "true") return;
|
|
||||||
const pagesToInvalidate = new Set<string>(
|
|
||||||
changes.flatMap((change) => this.getUrlFromEndpointChange(change))
|
|
||||||
);
|
|
||||||
|
|
||||||
for (const url of pagesToInvalidate) {
|
|
||||||
this.responseCache.delete(url);
|
|
||||||
this.logger.log("Invalidated cache for", url);
|
|
||||||
try {
|
|
||||||
await fetch(`http://${import.meta.env.ASTRO_HOST}:${import.meta.env.ASTRO_PORT}${url}`);
|
|
||||||
} catch (e) {
|
|
||||||
this.logger.log("Revalidation fails for", url);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.logger.log("There are currently", this.responseCache.size, "responses in cache.");
|
|
||||||
if (this.initialized) {
|
|
||||||
this.scheduleSave();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private scheduleSave() {
|
|
||||||
if (this.scheduleSaveTimeout) {
|
|
||||||
clearTimeout(this.scheduleSaveTimeout);
|
|
||||||
}
|
|
||||||
this.scheduleSaveTimeout = setTimeout(() => {
|
|
||||||
this.save();
|
|
||||||
}, 10_000);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async save() {
|
|
||||||
if (!existsSync(ON_DISK_ROOT)) {
|
|
||||||
await mkdir(ON_DISK_ROOT, { recursive: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
const serializedResponses = await Promise.all(
|
|
||||||
[...this.responseCache].map(async ([key, value]) => [key, await serializeResponse(value)])
|
|
||||||
);
|
|
||||||
const serializedResponseCache = JSON.stringify(serializedResponses);
|
|
||||||
await writeFile(ON_DISK_RESPONSE_CACHE_FILE, serializedResponseCache, {
|
|
||||||
encoding: "utf-8",
|
|
||||||
});
|
|
||||||
this.logger.log("Saved", ON_DISK_RESPONSE_CACHE_FILE);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue