Skip to content

Commit

Permalink
fix: retry if notionhq client times out
Browse files Browse the repository at this point in the history
  • Loading branch information
hatton committed Jul 13, 2023
1 parent b26b697 commit 8a4383a
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 11 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"// typescript check": "",
"tsc": "tsc",
"// test out with a private sample notion db": "",
"large-site-test": "npm run ts -- -n $SIL_BLOOM_DOCS_NOTION_TOKEN -r $SIL_BLOOM_DOCS_NOTION_ROOT_PAGE --locales en,fr",
"pull-test-tagged": "npm run ts -- -n $DOCU_NOTION_INTEGRATION_TOKEN -r $DOCU_NOTION_TEST_ROOT_PAGE_ID --log-level debug --status-tag test",
"pull-sample-site": "npm run ts -- -n $DOCU_NOTION_INTEGRATION_TOKEN -r $DOCU_NOTION_SAMPLE_ROOT_PAGE --log-level debug",
"// test with a semi-stable/public site:": "",
Expand Down
10 changes: 8 additions & 2 deletions src/plugins/ColumnTransformer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { NotionAPI } from "notion-client";
import { NotionToMarkdown } from "notion-to-md";
import { ListBlockChildrenResponseResult } from "notion-to-md/build/types";
import { IGetBlockChildrenFn, IPlugin } from "./pluginTypes";
import { executeWithRateLimitAndRetries } from "../pull";

export const standardColumnTransformer: IPlugin = {
name: "standardColumnTransformer",
Expand Down Expand Up @@ -61,8 +62,13 @@ async function getColumnWidth(
): Promise<string> {
const unofficialNotionClient = new NotionAPI();
const blockId = block.id;
// Yes, it is odd to call 'getPage' for a block, but that's how we access the format info.
const recordMap = await unofficialNotionClient.getPage(blockId);
const recordMap = await executeWithRateLimitAndRetries(
`unofficialNotionClient.getPage(${blockId}) in getColumnWidth()`,
() => {
// Yes, it is odd to call 'getPage' for a block, but that's how we access the format info.
return unofficialNotionClient.getPage(blockId);
}
);
const blockResult = recordMap.block[blockId];

// ENHANCE: could we use https://github.com/NotionX/react-notion-x/tree/master/packages/notion-types
Expand Down
57 changes: 48 additions & 9 deletions src/pull.ts
Original file line number Diff line number Diff line change
Expand Up @@ -256,11 +256,49 @@ const notionLimiter = new RateLimiter({
let notionClient: Client;

async function getPageMetadata(id: string): Promise<GetPageResponse> {
return await executeWithRateLimitAndRetries(`pages.retrieve(${id})`, () => {
return notionClient.pages.retrieve({
page_id: id,
});
});
}

// While everything works fine locally, on Github Actions we are getting a lot of timeouts, so
// we're trying this extra retry-able wrapper.
export async function executeWithRateLimitAndRetries<T>(
label: string,
asyncFunction: () => Promise<T>
): Promise<T> {
await rateLimit();
const kRetries = 10;
let lastException = undefined;
for (let i = 0; i < kRetries; i++) {
try {
return await asyncFunction();
} catch (e: any) {
lastException = e;
if (
e?.code === "notionhq_client_request_timeout" ||
e.message.includes("timeout") ||
e.message.includes("Timeout") ||
e.message.includes("limit") ||
e.message.includes("Limit")
) {
const secondsToWait = i + 1;
info(
`While doing "${label}", got error "${
e.message as string
}". Will retry after ${secondsToWait}s...`
);
await new Promise(resolve => setTimeout(resolve, 1000 * secondsToWait));
} else {
throw e;
}
}
}

return await notionClient.pages.retrieve({
page_id: id,
});
error(`Error: could not complete "${label}" after ${kRetries} retries.`);
throw lastException;
}

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

// Note: there is a now a collectPaginatedAPI() in the notion client, so
// we could switch to using that (I don't know if it does rate limiting?)
do {
await rateLimit();

const response: ListBlockChildrenResponse =
await notionClient.blocks.children.list({
start_cursor: start_cursor,
block_id: id,
await executeWithRateLimitAndRetries(`getBlockChildren(${id})`, () => {
return notionClient.blocks.children.list({
start_cursor: start_cursor as string | undefined,
block_id: id,
});
});

if (!overallResult) {
overallResult = response;
} else {
Expand Down

0 comments on commit 8a4383a

Please sign in to comment.