From 8e8749382e3da504046e71e8d19ae4c0bf434dee Mon Sep 17 00:00:00 2001 From: Henry Fontanier Date: Wed, 13 Sep 2023 15:01:20 +0200 Subject: [PATCH] fix: dont return anything from notion workflows (#1426) * enh: dont return anything from notion workflows * fix migration script * use date col * unused import * fix logic * outdated comment * remove upsert activity result type * fix case when notion is circular --- .../20230906_notion_fill_parents_field.ts | 6 +- .../notion/lib/connectors_db_helpers.ts | 12 ++ .../src/connectors/notion/lib/parents.ts | 110 +++++++----- .../connectors/notion/temporal/activities.ts | 74 +++++--- .../connectors/notion/temporal/workflows.ts | 163 +++++------------- connectors/src/lib/models.ts | 12 ++ 6 files changed, 194 insertions(+), 183 deletions(-) diff --git a/connectors/migrations/20230906_notion_fill_parents_field.ts b/connectors/migrations/20230906_notion_fill_parents_field.ts index a2956ff86f10..6bea5741ec45 100644 --- a/connectors/migrations/20230906_notion_fill_parents_field.ts +++ b/connectors/migrations/20230906_notion_fill_parents_field.ts @@ -66,7 +66,11 @@ async function updateParentsFieldForConnector(connector: Connector) { }); // update all parents fields for all pages and databases - await updateAllParentsFields(connector, [...pages, ...databases]); + await updateAllParentsFields( + connector, + pages.map((p) => p.notionPageId), + databases.map((d) => d.notionDatabaseId) + ); } main() diff --git a/connectors/src/connectors/notion/lib/connectors_db_helpers.ts b/connectors/src/connectors/notion/lib/connectors_db_helpers.ts index 31b776f9423b..aece7b744c4b 100644 --- a/connectors/src/connectors/notion/lib/connectors_db_helpers.ts +++ b/connectors/src/connectors/notion/lib/connectors_db_helpers.ts @@ -12,6 +12,7 @@ export async function upsertNotionPageInConnectorsDb({ notionUrl, lastUpsertedTs, skipReason, + lastCreatedOrMovedRunTs, }: { dataSourceInfo: DataSourceInfo; notionPageId: string; @@ -22,6 +23,7 @@ export async function upsertNotionPageInConnectorsDb({ notionUrl?: string | null; lastUpsertedTs?: number; skipReason?: string; + lastCreatedOrMovedRunTs?: number; }): Promise { const connector = await Connector.findOne({ where: { @@ -48,6 +50,7 @@ export async function upsertNotionPageInConnectorsDb({ notionUrl?: string; lastUpsertedTs?: Date; skipReason?: string; + lastCreatedOrMovedRunTs?: Date; } = { lastSeenTs: new Date(lastSeenTs), }; @@ -69,6 +72,9 @@ export async function upsertNotionPageInConnectorsDb({ if (notionUrl) { updateParams.notionUrl = notionUrl; } + if (lastCreatedOrMovedRunTs) { + updateParams.lastCreatedOrMovedRunTs = new Date(lastCreatedOrMovedRunTs); + } if (page) { return page.update(updateParams); @@ -123,6 +129,7 @@ export async function upsertNotionDatabaseInConnectorsDb({ title, notionUrl, skipReason, + lastCreatedOrMovedRunTs, }: { dataSourceInfo: DataSourceInfo; notionDatabaseId: string; @@ -132,6 +139,7 @@ export async function upsertNotionDatabaseInConnectorsDb({ title?: string | null; notionUrl?: string | null; skipReason?: string; + lastCreatedOrMovedRunTs?: number; }): Promise { const connector = await Connector.findOne({ where: { @@ -157,6 +165,7 @@ export async function upsertNotionDatabaseInConnectorsDb({ title?: string; notionUrl?: string; skipReason?: string; + lastCreatedOrMovedRunTs?: Date; } = { lastSeenTs: new Date(lastSeenTs), }; @@ -175,6 +184,9 @@ export async function upsertNotionDatabaseInConnectorsDb({ if (notionUrl) { updateParams.notionUrl = notionUrl; } + if (lastCreatedOrMovedRunTs) { + updateParams.lastCreatedOrMovedRunTs = new Date(lastCreatedOrMovedRunTs); + } if (database) { return database.update(updateParams); diff --git a/connectors/src/connectors/notion/lib/parents.ts b/connectors/src/connectors/notion/lib/parents.ts index 874342c068c7..645626356ae9 100644 --- a/connectors/src/connectors/notion/lib/parents.ts +++ b/connectors/src/connectors/notion/lib/parents.ts @@ -1,4 +1,5 @@ import memoize from "lodash.memoize"; +import PQueue from "p-queue"; import { getDatabaseChildrenOf, @@ -78,34 +79,42 @@ export const getParents = memoize( export async function updateAllParentsFields( dataSourceConfig: DataSourceConfig, - pageOrDbs: (NotionPage | NotionDatabase)[], + createdOrMovedNotionPageIds: string[], + createdOrMovedNotionDatabaseIds: string[], memoizationKey?: string ): Promise { /* Computing all descendants, then updating, ensures the field is updated only once per page, limiting the load on the Datasource */ - const pagesToUpdate = await getPagesToUpdate(pageOrDbs, dataSourceConfig); + const pageIdsToUpdate = await getPagesToUpdate( + createdOrMovedNotionPageIds, + createdOrMovedNotionDatabaseIds, + dataSourceConfig + ); // Update everybody's parents field. Use of a memoization key to control // sharing memoization across updateAllParentsFields calls, which // can be desired or not depending on the use case - for (let i = 0; i < pagesToUpdate.length; i += 16) { - const chunk = pagesToUpdate.slice(i, i + 16); - // updates are done in batches of 16 - const promises = chunk.map(async (page) => { - const parents = await getParents( - dataSourceConfig, - page.notionPageId, - memoizationKey - ); - await updateDocumentParentsField( - dataSourceConfig, - `notion-${page.notionPageId}`, - parents - ); - }); - await Promise.all(promises); + const q = new PQueue({ concurrency: 16 }); + const promises: Promise[] = []; + for (const pageId of pageIdsToUpdate) { + promises.push( + q.add(async () => { + const parents = await getParents( + dataSourceConfig, + pageId, + memoizationKey + ); + await updateDocumentParentsField( + dataSourceConfig, + `notion-${pageId}`, + parents + ); + }) + ); } - return pagesToUpdate.length; + + await Promise.all(promises); + return pageIdsToUpdate.size; } /** Get ids of all pages whose parents field should be updated: initial pages in @@ -116,39 +125,60 @@ export async function updateAllParentsFields( * updated */ async function getPagesToUpdate( - pageOrDbs: (NotionPage | NotionDatabase)[], + createdOrMovedNotionPageIds: string[], + createdOrMovedNotionDatabaseIds: string[], dataSourceConfig: DataSourceConfig -): Promise { - const pagesToUpdate: NotionPage[] = []; - - let i = 0; - while (i < pageOrDbs.length) { - // Visit next document and if it's a page add it to update list - const pageOrDb = pageOrDbs[i++] as NotionPage | NotionDatabase; - const pageOrDbId = notionPageOrDbId(pageOrDb); - if ((pageOrDb as NotionPage).notionPageId) { - pagesToUpdate.push(pageOrDb as NotionPage); +): Promise> { + const pageIdsToUpdate: Set = new Set([ + ...createdOrMovedNotionPageIds, + ]); + + // we need to look at all descendants of these objects, and add + // those that are pages to pageIdsToUpdate + const toProcess = new Set([ + ...createdOrMovedNotionPageIds, + ...createdOrMovedNotionDatabaseIds, + ]); + + const shift = () => { + for (const pageOrDbId of toProcess) { + toProcess.delete(pageOrDbId); + return pageOrDbId; + } + }; + const visited = new Set(); + + while (toProcess.size > 0) { + const pageOrDbIdToProcess = shift() as string; // guaranteed to be defined as toUpdate.size > 0 + visited.add(pageOrDbIdToProcess); + + const pageChildren = await getPageChildrenOf( + dataSourceConfig, + pageOrDbIdToProcess + ); + + // add page children to pageIdsToUpdate + for (const child of pageChildren) { + const childId = notionPageOrDbId(child); + pageIdsToUpdate.add(childId); } - // Get children of the document - const pageChildren = await getPageChildrenOf(dataSourceConfig, pageOrDbId); const databaseChildren = await getDatabaseChildrenOf( dataSourceConfig, - pageOrDbId + pageOrDbIdToProcess ); - // If they haven't yet been visited, add them to documents visited - // and to the list of documents whose children should be fetched + // add all page and DB children to toProcess for (const child of [...pageChildren, ...databaseChildren]) { - if ( - !pageOrDbs.some((d) => notionPageOrDbId(d) === notionPageOrDbId(child)) - ) { - pageOrDbs.push(child); + if (visited.has(notionPageOrDbId(child))) { + continue; } + const childId = notionPageOrDbId(child); + toProcess.add(childId); } } - return pagesToUpdate; + return pageIdsToUpdate; } function notionPageOrDbId(pageOrDb: NotionPage | NotionDatabase): string { diff --git a/connectors/src/connectors/notion/temporal/activities.ts b/connectors/src/connectors/notion/temporal/activities.ts index 2f74383da29e..0787a3cdf379 100644 --- a/connectors/src/connectors/notion/temporal/activities.ts +++ b/connectors/src/connectors/notion/temporal/activities.ts @@ -336,11 +336,6 @@ export async function notionGetToSyncActivity( }; } -export type UpsertActivityResult = { - pageOrDb: NotionPage | NotionDatabase | null; - createdOrMoved: boolean; -}; - export async function notionUpsertPageActivity( accessToken: string, pageId: string, @@ -348,7 +343,7 @@ export async function notionUpsertPageActivity( runTimestamp: number, loggerArgs: Record, isFullSync: boolean -): Promise { +): Promise { const localLogger = logger.child({ ...loggerArgs, pageId }); const notionPage = await getNotionPageFromConnectorsDb( @@ -360,7 +355,7 @@ export async function notionUpsertPageActivity( if (alreadySeenInRun) { localLogger.info("Skipping page already seen in this run"); - return { pageOrDb: notionPage, createdOrMoved: false }; + return; } const isSkipped = !!notionPage?.skipReason; @@ -370,7 +365,7 @@ export async function notionUpsertPageActivity( { skipReason: notionPage.skipReason }, "Skipping page with skip reason" ); - return { pageOrDb: notionPage, createdOrMoved: false }; + return; } let upsertTs: number | undefined = undefined; @@ -383,7 +378,7 @@ export async function notionUpsertPageActivity( if (parsedPage && parsedPage.rendered.length > MAX_DOCUMENT_TXT_LEN) { localLogger.info("Skipping page with too large body"); - const newNotionPage = await upsertNotionPageInConnectorsDb({ + await upsertNotionPageInConnectorsDb({ dataSourceInfo: dataSourceConfig, notionPageId: pageId, lastSeenTs: runTimestamp, @@ -393,8 +388,9 @@ export async function notionUpsertPageActivity( notionUrl: parsedPage ? parsedPage.url : null, lastUpsertedTs: upsertTs, skipReason: "body_too_large", + lastCreatedOrMovedRunTs: createdOrMoved ? runTimestamp : undefined, }); - return { pageOrDb: newNotionPage, createdOrMoved }; + return; } if (parsedPage && parsedPage.hasBody) { @@ -425,7 +421,7 @@ export async function notionUpsertPageActivity( } localLogger.info("notionUpsertPageActivity: Upserting notion page in DB."); - const newNotionPage = await upsertNotionPageInConnectorsDb({ + await upsertNotionPageInConnectorsDb({ dataSourceInfo: dataSourceConfig, notionPageId: pageId, lastSeenTs: runTimestamp, @@ -434,8 +430,9 @@ export async function notionUpsertPageActivity( title: parsedPage ? parsedPage.title : null, notionUrl: parsedPage ? parsedPage.url : null, lastUpsertedTs: upsertTs, + lastCreatedOrMovedRunTs: createdOrMoved ? runTimestamp : undefined, }); - return { pageOrDb: newNotionPage, createdOrMoved }; + return; } export async function notionUpsertDatabaseActivity( @@ -444,7 +441,7 @@ export async function notionUpsertDatabaseActivity( dataSourceConfig: DataSourceConfig, runTimestamp: number, loggerArgs: Record -): Promise { +): Promise { const localLogger = logger.child({ ...loggerArgs, databaseId }); const notionDatabase = await getNotionDatabaseFromConnectorsDb( @@ -457,7 +454,7 @@ export async function notionUpsertDatabaseActivity( if (alreadySeenInRun) { localLogger.info("Skipping database already seen in this run"); - return { pageOrDb: notionDatabase, createdOrMoved: false }; + return; } const isSkipped = !!notionDatabase?.skipReason; @@ -467,7 +464,7 @@ export async function notionUpsertDatabaseActivity( { skipReason: notionDatabase.skipReason }, "Skipping database with skip reason" ); - return { pageOrDb: notionDatabase, createdOrMoved: false }; + return; } localLogger.info( @@ -480,7 +477,7 @@ export async function notionUpsertDatabaseActivity( parsedDb?.parentType !== notionDatabase?.parentType || parsedDb?.parentId !== notionDatabase?.parentId; - const newNotionDb = await upsertNotionDatabaseInConnectorsDb({ + await upsertNotionDatabaseInConnectorsDb({ dataSourceInfo: dataSourceConfig, notionDatabaseId: databaseId, lastSeenTs: runTimestamp, @@ -488,8 +485,8 @@ export async function notionUpsertDatabaseActivity( parentId: parsedDb ? parsedDb.parentId : null, title: parsedDb ? parsedDb.title : null, notionUrl: parsedDb ? parsedDb.url : null, + lastCreatedOrMovedRunTs: createdOrMoved ? runTimestamp : undefined, }); - return { pageOrDb: newNotionDb, createdOrMoved: createdOrMoved }; } export async function saveSuccessSyncActivity( @@ -964,22 +961,51 @@ export async function garbageCollectActivity( export async function updateParentsFieldsActivity( dataSourceConfig: DataSourceConfig, - activitiesResults: UpsertActivityResult[], + runTimestamp: number, activityExecutionTimestamp: number ) { const localLogger = logger.child({ workspaceId: dataSourceConfig.workspaceId, dataSourceName: dataSourceConfig.dataSourceName, }); - // Get documents whose path changed (created or moved) If there is - // createdOrMoved, then the document cannot be null thus the cast is safe - const documents = activitiesResults - .filter((aRes) => aRes.createdOrMoved) - .map((aRes) => aRes.pageOrDb) as (NotionPage | NotionDatabase)[]; + + const connector = await Connector.findOne({ + where: { + type: "notion", + workspaceId: dataSourceConfig.workspaceId, + dataSourceName: dataSourceConfig.dataSourceName, + }, + }); + if (!connector) { + throw new Error("Could not find connector"); + } + + const notionPageIds = ( + await NotionPage.findAll({ + where: { + connectorId: connector.id, + lastCreatedOrMovedRunTs: runTimestamp, + }, + attributes: ["notionPageId"], + }) + ).map((page) => page.notionPageId); + + const notionDatabaseIds = ( + await NotionDatabase.findAll({ + where: { + connectorId: connector.id, + lastCreatedOrMovedRunTs: runTimestamp, + }, + attributes: ["notionDatabaseId"], + }) + ).map((db) => db.notionDatabaseId); + const nbUpdated = await updateAllParentsFields( dataSourceConfig, - documents, + notionPageIds, + notionDatabaseIds, activityExecutionTimestamp.toString() ); + localLogger.info({ nbUpdated }, "Updated parents fields."); } diff --git a/connectors/src/connectors/notion/temporal/workflows.ts b/connectors/src/connectors/notion/temporal/workflows.ts index 4f6818def7f0..41e39be4e4ba 100644 --- a/connectors/src/connectors/notion/temporal/workflows.ts +++ b/connectors/src/connectors/notion/temporal/workflows.ts @@ -10,7 +10,6 @@ import { import PQueue from "p-queue"; import type * as activities from "@connectors/connectors/notion/temporal/activities"; -import { UpsertActivityResult } from "@connectors/connectors/notion/temporal/activities"; import { DataSourceConfig } from "@connectors/types/data_source_config"; import { getWorkflowId } from "./utils"; @@ -95,12 +94,7 @@ export async function notionSyncWorkflow( concurrency: MAX_CONCURRENT_CHILD_WORKFLOWS, }); - const promises: Promise< - { - upsertPageResults: UpsertActivityResult[]; - upsertDatabaseResults: UpsertActivityResult[]; - }[] - >[] = []; + const promises: Promise[] = []; // we go through each result page of the notion search API do { @@ -134,7 +128,7 @@ export async function notionSyncWorkflow( // the worflow that processes databases will itself trigger child workflows to process // batches of child pages. promises.push( - getUpsertResults({ + performUpserts({ dataSourceConfig, notionAccessToken, pageIds, @@ -150,18 +144,11 @@ export async function notionSyncWorkflow( } while (cursor); // wait for all child workflows to finish - const results = (await Promise.all(promises)).flat(); - - const pageUpsertResults = results.flatMap((r) => r.upsertPageResults); - const databaseUpsertResults = results.flatMap( - (r) => r.upsertDatabaseResults - ); - - const allResults = [...pageUpsertResults, ...databaseUpsertResults]; + await Promise.all(promises); await updateParentsFieldsActivity( dataSourceConfig, - allResults, + runTimestamp, new Date().getTime() ); @@ -204,12 +191,12 @@ export async function notionSyncResultPageWorkflow( pageIds: string[], runTimestamp: number, isBatchSync = false -): Promise<(UpsertActivityResult | void)[]> { +): Promise { const upsertQueue = new PQueue({ concurrency: MAX_PENDING_UPSERT_ACTIVITIES, }); - const promises: Promise[] = []; + const promises: Promise[] = []; for (const [pageIndex, pageId] of pageIds.entries()) { const loggerArgs = { @@ -231,7 +218,7 @@ export async function notionSyncResultPageWorkflow( ); } - return await Promise.all(promises); + await Promise.all(promises); } export async function notionSyncResultPageDatabaseWorkflow( @@ -241,10 +228,7 @@ export async function notionSyncResultPageDatabaseWorkflow( runTimestamp: number, isGarbageCollectionRun = false, isBatchSync = false -): Promise<{ - upsertPageResults: UpsertActivityResult[]; - upsertDatabaseResults: UpsertActivityResult[]; -}> { +): Promise { const upsertQueue = new PQueue({ concurrency: MAX_PENDING_UPSERT_ACTIVITIES, }); @@ -252,13 +236,7 @@ export async function notionSyncResultPageDatabaseWorkflow( concurrency: MAX_CONCURRENT_CHILD_WORKFLOWS, }); - const databaseUpsertPromises: Promise[] = []; - const resultsPromises: Promise< - { - upsertPageResults: UpsertActivityResult[]; - upsertDatabaseResults: UpsertActivityResult[]; - }[] - >[] = []; + let promises: Promise[] = []; for (const [databaseIndex, databaseId] of databaseIds.entries()) { const loggerArgs = { @@ -267,7 +245,7 @@ export async function notionSyncResultPageDatabaseWorkflow( databaseIndex, }; - databaseUpsertPromises.push( + promises.push( upsertQueue.add(() => notionUpsertDatabaseActivity( notionAccessToken, @@ -282,12 +260,8 @@ export async function notionSyncResultPageDatabaseWorkflow( // wait for all db upserts before moving on to the children pages // otherwise we don't have control over concurrency - const dbUpsertResults: UpsertActivityResult[] = []; - for (const result of await Promise.all(databaseUpsertPromises)) { - if (result) { - dbUpsertResults.push(result); - } - } + await Promise.all(promises); + promises = []; for (const databaseId of databaseIds) { let cursor: string | null = null; @@ -315,7 +289,7 @@ export async function notionSyncResultPageDatabaseWorkflow( }); cursor = nextCursor; pageIndex += 1; - const upsertResultsPromise = getUpsertResults({ + const upsertsPromise = performUpserts({ dataSourceConfig, notionAccessToken, pageIds, @@ -329,19 +303,14 @@ export async function notionSyncResultPageDatabaseWorkflow( childWorkflowsNameSuffix: `database-children-${databaseId}`, }); - resultsPromises.push(upsertResultsPromise); + promises.push(upsertsPromise); } while (cursor); } - const upsertResults = (await Promise.all(resultsPromises)).flat(); - - return { - upsertPageResults: upsertResults.flatMap((r) => r.upsertPageResults), - upsertDatabaseResults: dbUpsertResults, - }; + await Promise.all(promises); } -async function getUpsertResults({ +async function performUpserts({ dataSourceConfig, notionAccessToken, pageIds, @@ -365,19 +334,11 @@ async function getUpsertResults({ skipUpToDatePages: boolean; queue: PQueue; childWorkflowsNameSuffix?: string; -}): Promise< - { - upsertPageResults: UpsertActivityResult[]; - upsertDatabaseResults: UpsertActivityResult[]; - }[] -> { +}): Promise { let pagesToSync: string[] = []; let databasesToSync: string[] = []; - const promises: Promise<{ - upsertPageResults: UpsertActivityResult[]; - upsertDatabaseResults: UpsertActivityResult[]; - }>[] = []; + const promises: Promise[] = []; if (isGarbageCollectionRun) { // Mark pages and databases as visited to avoid deleting them and return pages and databases @@ -396,7 +357,7 @@ async function getUpsertResults({ } if (!pagesToSync.length && !databasesToSync.length) { - return []; + return; } if (pagesToSync.length) { @@ -418,40 +379,19 @@ async function getUpsertResults({ } promises.push( - queue - .add(() => - executeChild(notionSyncResultPageWorkflow, { - workflowId, - args: [ - dataSourceConfig, - notionAccessToken, - batch, - runTimestamp, - isBatchSync, - ], - parentClosePolicy: - ParentClosePolicy.PARENT_CLOSE_POLICY_TERMINATE, - }) - ) - .then((r) => { - if (!r) { - return { - upsertPageResults: [], - upsertDatabaseResults: [], - }; - } - const pageResults: UpsertActivityResult[] = []; - for (const result of r) { - if (result) { - pageResults.push(result); - } - } - - return { - upsertPageResults: pageResults, - upsertDatabaseResults: [], - }; + queue.add(() => + executeChild(notionSyncResultPageWorkflow, { + workflowId, + args: [ + dataSourceConfig, + notionAccessToken, + batch, + runTimestamp, + isBatchSync, + ], + parentClosePolicy: ParentClosePolicy.PARENT_CLOSE_POLICY_TERMINATE, }) + ) ); } } @@ -478,36 +418,23 @@ async function getUpsertResults({ } promises.push( - queue - .add(() => - executeChild(notionSyncResultPageDatabaseWorkflow, { - workflowId, - args: [ - dataSourceConfig, - notionAccessToken, - batch, - runTimestamp, - isGarbageCollectionRun, - isBatchSync, - ], - parentClosePolicy: - ParentClosePolicy.PARENT_CLOSE_POLICY_TERMINATE, - }) - ) - .then((r) => { - if (!r) { - return { - upsertPageResults: [], - upsertDatabaseResults: [], - }; - } - return r; + queue.add(() => + executeChild(notionSyncResultPageDatabaseWorkflow, { + workflowId, + args: [ + dataSourceConfig, + notionAccessToken, + batch, + runTimestamp, + isGarbageCollectionRun, + isBatchSync, + ], + parentClosePolicy: ParentClosePolicy.PARENT_CLOSE_POLICY_TERMINATE, }) + ) ); } } - const results = await Promise.all(promises); - - return results; + await Promise.all(promises); } diff --git a/connectors/src/lib/models.ts b/connectors/src/lib/models.ts index a31b31da9f22..f327d01272a2 100644 --- a/connectors/src/lib/models.ts +++ b/connectors/src/lib/models.ts @@ -436,6 +436,7 @@ export class NotionPage extends Model< declare notionPageId: string; declare lastSeenTs: Date; declare lastUpsertedTs?: Date; + declare lastCreatedOrMovedRunTs: CreationOptional; declare skipReason?: string | null; @@ -477,6 +478,10 @@ NotionPage.init( type: DataTypes.DATE, allowNull: true, }, + lastCreatedOrMovedRunTs: { + type: DataTypes.DATE, + allowNull: true, + }, skipReason: { type: DataTypes.STRING, allowNull: true, @@ -509,6 +514,7 @@ NotionPage.init( { fields: ["connectorId"] }, { fields: ["lastSeenTs"] }, { fields: ["parentId"] }, + { fields: ["lastCreatedOrMovedRunTs"] }, { fields: ["titleSearchVector"], using: "gist", @@ -530,6 +536,7 @@ export class NotionDatabase extends Model< declare notionDatabaseId: string; declare lastSeenTs: Date; + declare lastCreatedOrMovedRunTs: CreationOptional; declare skipReason?: string | null; @@ -567,6 +574,10 @@ NotionDatabase.init( type: DataTypes.DATE, allowNull: false, }, + lastCreatedOrMovedRunTs: { + type: DataTypes.DATE, + allowNull: true, + }, skipReason: { type: DataTypes.STRING, allowNull: true, @@ -598,6 +609,7 @@ NotionDatabase.init( { fields: ["notionDatabaseId", "connectorId"], unique: true }, { fields: ["connectorId", "skipReason"] }, { fields: ["lastSeenTs"] }, + { fields: ["lastCreatedOrMovedRunTs"] }, { fields: ["parentId"] }, { fields: ["titleSearchVector"],