Skip to content

Commit 8a4383a

Browse files
committed
fix: retry if notionhq client times out
1 parent b26b697 commit 8a4383a

File tree

3 files changed

+57
-11
lines changed

3 files changed

+57
-11
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
"// typescript check": "",
1212
"tsc": "tsc",
1313
"// test out with a private sample notion db": "",
14+
"large-site-test": "npm run ts -- -n $SIL_BLOOM_DOCS_NOTION_TOKEN -r $SIL_BLOOM_DOCS_NOTION_ROOT_PAGE --locales en,fr",
1415
"pull-test-tagged": "npm run ts -- -n $DOCU_NOTION_INTEGRATION_TOKEN -r $DOCU_NOTION_TEST_ROOT_PAGE_ID --log-level debug --status-tag test",
1516
"pull-sample-site": "npm run ts -- -n $DOCU_NOTION_INTEGRATION_TOKEN -r $DOCU_NOTION_SAMPLE_ROOT_PAGE --log-level debug",
1617
"// test with a semi-stable/public site:": "",

src/plugins/ColumnTransformer.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { NotionAPI } from "notion-client";
22
import { NotionToMarkdown } from "notion-to-md";
33
import { ListBlockChildrenResponseResult } from "notion-to-md/build/types";
44
import { IGetBlockChildrenFn, IPlugin } from "./pluginTypes";
5+
import { executeWithRateLimitAndRetries } from "../pull";
56

67
export const standardColumnTransformer: IPlugin = {
78
name: "standardColumnTransformer",
@@ -61,8 +62,13 @@ async function getColumnWidth(
6162
): Promise<string> {
6263
const unofficialNotionClient = new NotionAPI();
6364
const blockId = block.id;
64-
// Yes, it is odd to call 'getPage' for a block, but that's how we access the format info.
65-
const recordMap = await unofficialNotionClient.getPage(blockId);
65+
const recordMap = await executeWithRateLimitAndRetries(
66+
`unofficialNotionClient.getPage(${blockId}) in getColumnWidth()`,
67+
() => {
68+
// Yes, it is odd to call 'getPage' for a block, but that's how we access the format info.
69+
return unofficialNotionClient.getPage(blockId);
70+
}
71+
);
6672
const blockResult = recordMap.block[blockId];
6773

6874
// ENHANCE: could we use https://github.com/NotionX/react-notion-x/tree/master/packages/notion-types

src/pull.ts

Lines changed: 48 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -256,11 +256,49 @@ const notionLimiter = new RateLimiter({
256256
let notionClient: Client;
257257

258258
async function getPageMetadata(id: string): Promise<GetPageResponse> {
259+
return await executeWithRateLimitAndRetries(`pages.retrieve(${id})`, () => {
260+
return notionClient.pages.retrieve({
261+
page_id: id,
262+
});
263+
});
264+
}
265+
266+
// While everything works fine locally, on Github Actions we are getting a lot of timeouts, so
267+
// we're trying this extra retry-able wrapper.
268+
export async function executeWithRateLimitAndRetries<T>(
269+
label: string,
270+
asyncFunction: () => Promise<T>
271+
): Promise<T> {
259272
await rateLimit();
273+
const kRetries = 10;
274+
let lastException = undefined;
275+
for (let i = 0; i < kRetries; i++) {
276+
try {
277+
return await asyncFunction();
278+
} catch (e: any) {
279+
lastException = e;
280+
if (
281+
e?.code === "notionhq_client_request_timeout" ||
282+
e.message.includes("timeout") ||
283+
e.message.includes("Timeout") ||
284+
e.message.includes("limit") ||
285+
e.message.includes("Limit")
286+
) {
287+
const secondsToWait = i + 1;
288+
info(
289+
`While doing "${label}", got error "${
290+
e.message as string
291+
}". Will retry after ${secondsToWait}s...`
292+
);
293+
await new Promise(resolve => setTimeout(resolve, 1000 * secondsToWait));
294+
} else {
295+
throw e;
296+
}
297+
}
298+
}
260299

261-
return await notionClient.pages.retrieve({
262-
page_id: id,
263-
});
300+
error(`Error: could not complete "${label}" after ${kRetries} retries.`);
301+
throw lastException;
264302
}
265303

266304
async function rateLimit() {
@@ -275,18 +313,19 @@ async function getBlockChildren(id: string): Promise<NotionBlock[]> {
275313
// the first response we get, then keep adding to its array of blocks
276314
// with each subsequent response
277315
let overallResult: ListBlockChildrenResponse | undefined = undefined;
278-
let start_cursor = undefined;
316+
let start_cursor: string | undefined | null = undefined;
279317

280318
// Note: there is a now a collectPaginatedAPI() in the notion client, so
281319
// we could switch to using that (I don't know if it does rate limiting?)
282320
do {
283-
await rateLimit();
284-
285321
const response: ListBlockChildrenResponse =
286-
await notionClient.blocks.children.list({
287-
start_cursor: start_cursor,
288-
block_id: id,
322+
await executeWithRateLimitAndRetries(`getBlockChildren(${id})`, () => {
323+
return notionClient.blocks.children.list({
324+
start_cursor: start_cursor as string | undefined,
325+
block_id: id,
326+
});
289327
});
328+
290329
if (!overallResult) {
291330
overallResult = response;
292331
} else {

0 commit comments

Comments
 (0)