Merge branch 'main' into stevenh/perf-investigations

This commit is contained in:
Steven H
2025-06-16 23:17:12 +01:00
committed by GitHub
3 changed files with 125 additions and 100 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"gitbook-v2": patch
---
Don't cache unexpected API errors for more than a few minutes.
+103 -100
View File
@@ -9,7 +9,7 @@ import { getCacheTag, getComputedContentSourceCacheTags } from '@gitbook/cache-t
import { GITBOOK_API_TOKEN, GITBOOK_API_URL, GITBOOK_USER_AGENT } from '@v2/lib/env';
import { unstable_cacheLife as cacheLife, unstable_cacheTag as cacheTag } from 'next/cache';
import { cache } from '../cache';
import { DataFetcherError, wrapDataFetcherError } from './errors';
import { DataFetcherError, wrapCacheDataFetcherError } from './errors';
import type { GitBookDataFetcher } from './types';
interface DataFetcherInput {
@@ -206,8 +206,8 @@ export function createDataFetcher(
const getUserById = cache(async (input: DataFetcherInput, params: { userId: string }) => {
'use cache';
return trace(`getUserById(${params.userId})`, async () => {
return wrapDataFetcherError(async () => {
return wrapCacheDataFetcherError(async () => {
return trace(`getUserById(${params.userId})`, async () => {
const api = apiClient(input);
const res = await api.users.getUserById(params.userId, {
...noCacheFetchOptions,
@@ -229,8 +229,8 @@ const getSpace = cache(
})
);
return trace(`getSpace(${params.spaceId}, ${params.shareKey})`, async () => {
return wrapDataFetcherError(async () => {
return wrapCacheDataFetcherError(async () => {
return trace(`getSpace(${params.spaceId}, ${params.shareKey})`, async () => {
const api = apiClient(input);
const res = await api.spaces.getSpaceById(
params.spaceId,
@@ -260,20 +260,23 @@ const getChangeRequest = cache(
})
);
return trace(`getChangeRequest(${params.spaceId}, ${params.changeRequestId})`, async () => {
return wrapDataFetcherError(async () => {
const api = apiClient(input);
const res = await api.spaces.getChangeRequestById(
params.spaceId,
params.changeRequestId,
{
...noCacheFetchOptions,
}
);
cacheTag(...getCacheTagsFromResponse(res));
cacheLife('minutes');
return res.data;
});
return wrapCacheDataFetcherError(async () => {
return trace(
`getChangeRequest(${params.spaceId}, ${params.changeRequestId})`,
async () => {
const api = apiClient(input);
const res = await api.spaces.getChangeRequestById(
params.spaceId,
params.changeRequestId,
{
...noCacheFetchOptions,
}
);
cacheTag(...getCacheTagsFromResponse(res));
cacheLife('minutes');
return res.data;
}
);
});
}
);
@@ -284,8 +287,8 @@ const getRevision = cache(
params: { spaceId: string; revisionId: string; metadata: boolean }
) => {
'use cache';
return trace(`getRevision(${params.spaceId}, ${params.revisionId})`, async () => {
return wrapDataFetcherError(async () => {
return wrapCacheDataFetcherError(async () => {
return trace(`getRevision(${params.spaceId}, ${params.revisionId})`, async () => {
const api = apiClient(input);
const res = await api.spaces.getRevisionById(
params.spaceId,
@@ -311,8 +314,8 @@ const getRevisionPages = cache(
params: { spaceId: string; revisionId: string; metadata: boolean }
) => {
'use cache';
return trace(`getRevisionPages(${params.spaceId}, ${params.revisionId})`, async () => {
return wrapDataFetcherError(async () => {
return wrapCacheDataFetcherError(async () => {
return trace(`getRevisionPages(${params.spaceId}, ${params.revisionId})`, async () => {
const api = apiClient(input);
const res = await api.spaces.listPagesInRevisionById(
params.spaceId,
@@ -338,10 +341,10 @@ const getRevisionFile = cache(
params: { spaceId: string; revisionId: string; fileId: string }
) => {
'use cache';
return trace(
`getRevisionFile(${params.spaceId}, ${params.revisionId}, ${params.fileId})`,
async () => {
return wrapDataFetcherError(async () => {
return wrapCacheDataFetcherError(async () => {
return trace(
`getRevisionFile(${params.spaceId}, ${params.revisionId}, ${params.fileId})`,
async () => {
const api = apiClient(input);
const res = await api.spaces.getFileInRevisionById(
params.spaceId,
@@ -355,9 +358,9 @@ const getRevisionFile = cache(
cacheTag(...getCacheTagsFromResponse(res));
cacheLife('max');
return res.data;
});
}
);
}
);
});
}
);
@@ -367,10 +370,10 @@ const getRevisionPageMarkdown = cache(
params: { spaceId: string; revisionId: string; pageId: string }
) => {
'use cache';
return trace(
`getRevisionPageMarkdown(${params.spaceId}, ${params.revisionId}, ${params.pageId})`,
async () => {
return wrapDataFetcherError(async () => {
return wrapCacheDataFetcherError(async () => {
return trace(
`getRevisionPageMarkdown(${params.spaceId}, ${params.revisionId}, ${params.pageId})`,
async () => {
const api = apiClient(input);
const res = await api.spaces.getPageInRevisionById(
params.spaceId,
@@ -391,9 +394,9 @@ const getRevisionPageMarkdown = cache(
throw new DataFetcherError('Page is not a document', 404);
}
return res.data.markdown;
});
}
);
}
);
});
}
);
@@ -403,10 +406,10 @@ const getRevisionPageDocument = cache(
params: { spaceId: string; revisionId: string; pageId: string }
) => {
'use cache';
return trace(
`getRevisionPageDocument(${params.spaceId}, ${params.revisionId}, ${params.pageId})`,
async () => {
return wrapDataFetcherError(async () => {
return wrapCacheDataFetcherError(async () => {
return trace(
`getRevisionPageDocument(${params.spaceId}, ${params.revisionId}, ${params.pageId})`,
async () => {
const api = apiClient(input);
const res = await api.spaces.getPageDocumentInRevisionById(
params.spaceId,
@@ -424,9 +427,9 @@ const getRevisionPageDocument = cache(
cacheLife('max');
return res.data;
});
}
);
}
);
});
}
);
@@ -436,11 +439,11 @@ const getRevisionPageByPath = cache(
params: { spaceId: string; revisionId: string; path: string }
) => {
'use cache';
return trace(
`getRevisionPageByPath(${params.spaceId}, ${params.revisionId}, ${params.path})`,
async () => {
const encodedPath = encodeURIComponent(params.path);
return wrapDataFetcherError(async () => {
return wrapCacheDataFetcherError(async () => {
return trace(
`getRevisionPageByPath(${params.spaceId}, ${params.revisionId}, ${params.path})`,
async () => {
const encodedPath = encodeURIComponent(params.path);
const api = apiClient(input);
const res = await api.spaces.getPageInRevisionByPath(
params.spaceId,
@@ -454,17 +457,17 @@ const getRevisionPageByPath = cache(
cacheTag(...getCacheTagsFromResponse(res));
cacheLife('max');
return res.data;
});
}
);
}
);
});
}
);
const getDocument = cache(
async (input: DataFetcherInput, params: { spaceId: string; documentId: string }) => {
'use cache';
return trace(`getDocument(${params.spaceId}, ${params.documentId})`, async () => {
return wrapDataFetcherError(async () => {
return wrapCacheDataFetcherError(async () => {
return trace(`getDocument(${params.spaceId}, ${params.documentId})`, async () => {
const api = apiClient(input);
const res = await api.spaces.getDocumentById(
params.spaceId,
@@ -503,10 +506,10 @@ const getComputedDocument = cache(
)
);
return trace(
`getComputedDocument(${params.spaceId}, ${params.organizationId}, ${params.source.type}, ${params.seed})`,
async () => {
return wrapDataFetcherError(async () => {
return wrapCacheDataFetcherError(async () => {
return trace(
`getComputedDocument(${params.spaceId}, ${params.organizationId}, ${params.source.type}, ${params.seed})`,
async () => {
const api = apiClient(input);
const res = await api.spaces.getComputedDocument(
params.spaceId,
@@ -522,9 +525,9 @@ const getComputedDocument = cache(
cacheTag(...getCacheTagsFromResponse(res));
cacheLife('max');
return res.data;
});
}
);
}
);
});
}
);
@@ -534,10 +537,10 @@ const getReusableContent = cache(
params: { spaceId: string; revisionId: string; reusableContentId: string }
) => {
'use cache';
return trace(
`getReusableContent(${params.spaceId}, ${params.revisionId}, ${params.reusableContentId})`,
async () => {
return wrapDataFetcherError(async () => {
return wrapCacheDataFetcherError(async () => {
return trace(
`getReusableContent(${params.spaceId}, ${params.revisionId}, ${params.reusableContentId})`,
async () => {
const api = apiClient(input);
const res = await api.spaces.getReusableContentInRevisionById(
params.spaceId,
@@ -551,9 +554,9 @@ const getReusableContent = cache(
cacheTag(...getCacheTagsFromResponse(res));
cacheLife('max');
return res.data;
});
}
);
}
);
});
}
);
@@ -568,10 +571,10 @@ const getLatestOpenAPISpecVersionContent = cache(
})
);
return trace(
`getLatestOpenAPISpecVersionContent(${params.organizationId}, ${params.slug})`,
async () => {
return wrapDataFetcherError(async () => {
return wrapCacheDataFetcherError(async () => {
return trace(
`getLatestOpenAPISpecVersionContent(${params.organizationId}, ${params.slug})`,
async () => {
const api = apiClient(input);
const res = await api.orgs.getLatestOpenApiSpecVersionContent(
params.organizationId,
@@ -583,9 +586,9 @@ const getLatestOpenAPISpecVersionContent = cache(
cacheTag(...getCacheTagsFromResponse(res));
cacheLife('max');
return res.data;
});
}
);
}
);
});
}
);
@@ -602,10 +605,10 @@ const getPublishedContentSite = cache(
})
);
return trace(
`getPublishedContentSite(${params.organizationId}, ${params.siteId}, ${params.siteShareKey})`,
async () => {
return wrapDataFetcherError(async () => {
return wrapCacheDataFetcherError(async () => {
return trace(
`getPublishedContentSite(${params.organizationId}, ${params.siteId}, ${params.siteShareKey})`,
async () => {
const api = apiClient(input);
const res = await api.orgs.getPublishedContentSite(
params.organizationId,
@@ -620,9 +623,9 @@ const getPublishedContentSite = cache(
cacheTag(...getCacheTagsFromResponse(res));
cacheLife('days');
return res.data;
});
}
);
}
);
});
}
);
@@ -644,10 +647,10 @@ const getSiteRedirectBySource = cache(
})
);
return trace(
`getSiteRedirectBySource(${params.organizationId}, ${params.siteId}, ${params.siteShareKey}, ${params.source})`,
async () => {
return wrapDataFetcherError(async () => {
return wrapCacheDataFetcherError(async () => {
return trace(
`getSiteRedirectBySource(${params.organizationId}, ${params.siteId}, ${params.siteShareKey}, ${params.source})`,
async () => {
const api = apiClient(input);
const res = await api.orgs.getSiteRedirectBySource(
params.organizationId,
@@ -663,9 +666,9 @@ const getSiteRedirectBySource = cache(
cacheTag(...getCacheTagsFromResponse(res));
cacheLife('days');
return res.data;
});
}
);
}
);
});
}
);
@@ -679,8 +682,8 @@ const getEmbedByUrl = cache(
})
);
return trace(`getEmbedByUrl(${params.spaceId}, ${params.url})`, async () => {
return wrapDataFetcherError(async () => {
return wrapCacheDataFetcherError(async () => {
return trace(`getEmbedByUrl(${params.spaceId}, ${params.url})`, async () => {
const api = apiClient(input);
const res = await api.spaces.getEmbedByUrlInSpace(
params.spaceId,
@@ -712,10 +715,10 @@ const searchSiteContent = cache(
})
);
return trace(
`searchSiteContent(${params.organizationId}, ${params.siteId}, ${params.query})`,
async () => {
return wrapDataFetcherError(async () => {
return wrapCacheDataFetcherError(async () => {
return trace(
`searchSiteContent(${params.organizationId}, ${params.siteId}, ${params.query})`,
async () => {
const { organizationId, siteId, query, scope } = params;
const api = apiClient(input);
const res = await api.orgs.searchSiteContent(
@@ -733,9 +736,9 @@ const searchSiteContent = cache(
cacheTag(...getCacheTagsFromResponse(res));
cacheLife('hours');
return res.data.items;
});
}
);
}
);
});
}
);
@@ -752,8 +755,8 @@ const renderIntegrationUi = cache(
})
);
return trace(`renderIntegrationUi(${params.integrationName})`, async () => {
return wrapDataFetcherError(async () => {
return wrapCacheDataFetcherError(async () => {
return trace(`renderIntegrationUi(${params.integrationName})`, async () => {
const api = apiClient(input);
const res = await api.integrations.renderIntegrationUiWithPost(
params.integrationName,
@@ -1,4 +1,5 @@
import { GitBookAPIError } from '@gitbook/api';
import { unstable_cacheLife as cacheLife } from 'next/cache';
import type { DataFetcherErrorData, DataFetcherResponse } from './types';
export class DataFetcherError extends Error {
@@ -89,6 +90,22 @@ export async function wrapDataFetcherError<T>(
}
}
/**
* Wrap an async execution to handle errors and return a DataFetcherResponse.
* This should be used inside 'use cache' functions.
*/
export async function wrapCacheDataFetcherError<T>(
fn: () => Promise<T>
): Promise<DataFetcherResponse<T>> {
const result = await wrapDataFetcherError(fn);
if (result.error && result.error.code >= 500) {
// We don't want to cache errors for too long.
// as the API might
cacheLife('minutes');
}
return result;
}
/**
* Ignore some data fetcher errors.
*/