From cd16ccaf378caaf95f978c8dc99375cdb0b06490 Mon Sep 17 00:00:00 2001 From: Kyle Mathews Date: Wed, 20 Oct 2021 04:34:28 -0700 Subject: [PATCH] fix(gatsby): use lmdb.removeSync so getNode can't return deleted nodes (#33554) Co-authored-by: Ward Peeters (cherry picked from commit 98a843c0ba1db325bde2cbc8ac6906348ffe2dc3) # Conflicts: # packages/gatsby/src/datastore/lmdb/lmdb-datastore.ts --- .../src/datastore/lmdb/lmdb-datastore.ts | 49 +++++++++++++------ .../src/datastore/lmdb/updates/nodes.ts | 1 + 2 files changed, 36 insertions(+), 14 deletions(-) diff --git a/packages/gatsby/src/datastore/lmdb/lmdb-datastore.ts b/packages/gatsby/src/datastore/lmdb/lmdb-datastore.ts index 3a6f5e6f3d9ca..a323643db04e4 100644 --- a/packages/gatsby/src/datastore/lmdb/lmdb-datastore.ts +++ b/packages/gatsby/src/datastore/lmdb/lmdb-datastore.ts @@ -1,4 +1,4 @@ -import { RootDatabase, open } from "lmdb-store" +import { RootDatabase, open, ArrayLikeIterable } from "lmdb-store" // import { performance } from "perf_hooks" import { ActionsUnion, IGatsbyNode } from "../../redux/types" import { updateNodes } from "./updates/nodes" @@ -27,6 +27,8 @@ const lmdbDatastore = { getNodesByType, } +const preSyncDeletedNodeIdsCache = new Set() + function getDefaultDbPath(): string { const dbFileName = process.env.NODE_ENV === `test` @@ -122,10 +124,8 @@ function iterateNodes(): GatsbyIterable { return new GatsbyIterable( nodesDb .getKeys({ snapshot: false }) - .map( - nodeId => (typeof nodeId === `string` ? getNode(nodeId) : undefined)! - ) - .filter(Boolean) + .map(nodeId => (typeof nodeId === `string` ? getNode(nodeId) : undefined)) + .filter(Boolean) as ArrayLikeIterable ) } @@ -134,13 +134,16 @@ function iterateNodesByType(type: string): GatsbyIterable { return new GatsbyIterable( nodesByType .getValues(type) - .map(nodeId => getNode(nodeId)!) - .filter(Boolean) + .map(nodeId => getNode(nodeId)) + .filter(Boolean) as ArrayLikeIterable ) } function getNode(id: string): IGatsbyNode | undefined { - if (!id) return undefined + if (!id || preSyncDeletedNodeIdsCache.has(id)) { + return undefined + } + const { nodes } = getDatabases() return nodes.get(id) } @@ -151,9 +154,11 @@ function getTypes(): Array { function countNodes(typeName?: string): number { if (!typeName) { - const stats = getDatabases().nodes.getStats() - // @ts-ignore - return Number(stats.entryCount || 0) // FIXME: add -1 when restoring shared structures key + const stats = getDatabases().nodes.getStats() as { entryCount: number } + return Math.max( + Number(stats.entryCount) - preSyncDeletedNodeIdsCache.size, + 0 + ) // FIXME: add -1 when restoring shared structures key } const { nodesByType } = getDatabases() @@ -192,14 +197,30 @@ function updateDataStore(action: ActionsUnion): void { break } case `CREATE_NODE`: + case `DELETE_NODE`: case `ADD_FIELD_TO_NODE`: - case `ADD_CHILD_NODE_TO_PARENT_NODE`: - case `DELETE_NODE`: { + case `ADD_CHILD_NODE_TO_PARENT_NODE`: { const dbs = getDatabases() - lastOperationPromise = Promise.all([ + const operationPromise = Promise.all([ updateNodes(dbs.nodes, action), updateNodesByType(dbs.nodesByType, action), ]) + lastOperationPromise = operationPromise + + // if create is used in the same transaction as delete we should remove it from cache + if (action.type === `CREATE_NODE`) { + preSyncDeletedNodeIdsCache.delete(action.payload.id) + } + + if (action.type === `DELETE_NODE` && action.payload?.id) { + preSyncDeletedNodeIdsCache.add(action.payload.id) + operationPromise.then(() => { + // only clear if no other operations have been done in the meantime + if (lastOperationPromise === operationPromise) { + preSyncDeletedNodeIdsCache.clear() + } + }) + } } } } diff --git a/packages/gatsby/src/datastore/lmdb/updates/nodes.ts b/packages/gatsby/src/datastore/lmdb/updates/nodes.ts index a6049b5f20c53..ddad7f36c624c 100644 --- a/packages/gatsby/src/datastore/lmdb/updates/nodes.ts +++ b/packages/gatsby/src/datastore/lmdb/updates/nodes.ts @@ -17,6 +17,7 @@ export function updateNodes( if (action.payload) { return nodesDb.remove(action.payload.id) } + return false } }