Race condition in SSG (with possible solution)
wjohnsto opened this issue · 1 comments
I hit an issue when doing SSG in a Next.js app where my cached page data is not always complete. I'm using prepareReactRender
, and the temporary solution that seemed to work was to call prepareReactRender
twice. This indicates to me that the underlying issue is a race condition.
After doing some debugging I believe I have identified the area of code that has the race condition, and I have a solution for it. The race condition can be found in the ssr.ts file in gqless core. Here is the code in question:
let renderPromise: Promise<unknown> | unknown | undefined;
const interceptor = innerState.interceptorManager.createInterceptor();
let prevIgnoreCache = innerState.allowCache;
try {
innerState.allowCache = false;
renderPromise = render();
} finally {
innerState.interceptorManager.removeInterceptor(interceptor);
innerState.allowCache = prevIgnoreCache;
}
await Promise.all([
renderPromise,
...innerState.scheduler.pendingSelectionsGroupsPromises.values(),
]);
You can see in the code above that render()
returns a promise that is resolved in the Promise.all
. Note that this occurs after innerState.interceptorManager.removeInterceptor(interceptor);
. However, when you call removeInterceptor
the interceptor will no longer receive any new selections. So later on in that method when you look at interceptor.fetchSelections
it will only contain the selections that were able to be observed between the time you call render()
and removeInterceptor
. The following code appears to fix the issue:
const interceptor = innerState.interceptorManager.createInterceptor();
let prevIgnoreCache = innerState.allowCache;
try {
innerState.allowCache = false;
await render();
} finally {
innerState.interceptorManager.removeInterceptor(interceptor);
innerState.allowCache = prevIgnoreCache;
}
await Promise.all([
...innerState.scheduler.pendingSelectionsGroupsPromises.values(),
]);
I'm willing to submit a PR to fix this if necessary, just let me know.