From 957ce69d2ee855f8ef1afe397752e72d860443ce Mon Sep 17 00:00:00 2001 From: Tom Moor Date: Mon, 23 Feb 2026 22:14:03 -0500 Subject: [PATCH] perf: Move image download out of transaction (#11528) * perf: Move image download out of transaction * loop --- server/context.ts | 3 ++ server/queues/tasks/DocumentImportTask.ts | 35 +++++++++++------------ 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/server/context.ts b/server/context.ts index 908339ee2b..910465e9e5 100644 --- a/server/context.ts +++ b/server/context.ts @@ -3,6 +3,9 @@ import type { User } from "@server/models"; import type { APIContext } from "@server/types"; import { AuthenticationType } from "@server/types"; +/** + * Factory to create a new API context. + */ export function createContext({ user, authType = AuthenticationType.APP, diff --git a/server/queues/tasks/DocumentImportTask.ts b/server/queues/tasks/DocumentImportTask.ts index c0733a67df..2ae55179d8 100644 --- a/server/queues/tasks/DocumentImportTask.ts +++ b/server/queues/tasks/DocumentImportTask.ts @@ -3,8 +3,8 @@ import documentCreator from "@server/commands/documentCreator"; import documentImporter from "@server/commands/documentImporter"; import { createContext } from "@server/context"; import { User } from "@server/models"; -import { sequelize } from "@server/storage/database"; import FileStorage from "@server/storage/files"; +import { sequelize } from "@server/storage/database"; import { BaseTask, TaskPriority } from "./base/BaseTask"; type Props = { @@ -37,24 +37,23 @@ export default class DocumentImportTask extends BaseTask { }: Props): Promise { try { const content = await FileStorage.getFileBuffer(key); + const user = await User.findByPk(userId, { + rejectOnEmpty: true, + }); - const document = await sequelize.transaction(async (transaction) => { - const user = await User.findByPk(userId, { - rejectOnEmpty: true, - transaction, - }); + // Run document conversion and image downloading outside a transaction + const ctx = createContext({ user, ip }); - const ctx = createContext({ user, transaction, ip }); + const { text, state, title, icon } = await documentImporter({ + user, + fileName: sourceMetadata.fileName, + mimeType: sourceMetadata.mimeType, + content, + ctx, + }); - const { text, state, title, icon } = await documentImporter({ - user, - fileName: sourceMetadata.fileName, - mimeType: sourceMetadata.mimeType, - content, - ctx, - }); - - return documentCreator(ctx, { + const document = await sequelize.transaction(async (transaction) => + documentCreator(createContext({ ...ctx.context, transaction }), { sourceMetadata, title, icon, @@ -63,8 +62,8 @@ export default class DocumentImportTask extends BaseTask { publish, collectionId, parentDocumentId, - }); - }); + }) + ); return { documentId: document.id }; } catch (err) { return { error: err.message };