Skip to content

Commit

Permalink
delete api/files is now transactional.
Browse files Browse the repository at this point in the history
add namespace parameter for improved transaction logging
  • Loading branch information
daneryl committed Feb 10, 2025
1 parent b390b65 commit cfba0df
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 26 deletions.
2 changes: 1 addition & 1 deletion app/api/entities/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ export default app => {
await abort();
}
return req.body.entity ? saveResult : entity;
});
}, 'POST /api/entities');
res.json(result);
if (tenants.current().featureFlags.v1_transactions) {
req.emitToSessionSocket(
Expand Down
2 changes: 1 addition & 1 deletion app/api/files/jsRoutes.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ const routes = app => {
if (email) {
await mailer.send(email);
}
});
}, 'POST /api/public');
res.json(result);
}
);
Expand Down
31 changes: 19 additions & 12 deletions app/api/files/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { UserSchema } from 'shared/types/userType';
import { createError, handleError, validation } from '../utils';
import { files } from './files';
import { storage } from './storage';
import { withTransaction } from 'api/utils/withTransaction';

const checkEntityPermission = async (
file: FileType,
Expand Down Expand Up @@ -266,18 +267,24 @@ export default (app: Application) => {
}),

async (req: Request<{}, {}, {}, { _id: string }>, res) => {
const [fileToDelete] = await files.get({ _id: req.query._id });
if (
!fileToDelete ||
!(await checkEntityPermission(fileToDelete, permissionsContext.getUserInContext(), 'write'))
) {
throw createError('file not found', 404);
}

const [deletedFile] = await files.delete({ _id: req.query._id });
const thumbnailFileName = `${deletedFile._id}.jpg`;
await files.delete({ filename: thumbnailFileName });
res.json([deletedFile]);
await withTransaction(async () => {
const [fileToDelete] = await files.get({ _id: req.query._id });
if (
!fileToDelete ||
!(await checkEntityPermission(
fileToDelete,
permissionsContext.getUserInContext(),
'write'
))
) {
throw createError('file not found', 404);
}

const [deletedFile] = await files.delete({ _id: req.query._id });
const thumbnailFileName = `${deletedFile._id}.jpg`;
await files.delete({ filename: thumbnailFileName });
res.json([deletedFile]);
}, 'DELETE /api/files');
}
);

Expand Down
27 changes: 15 additions & 12 deletions app/api/utils/withTransaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,17 @@ const performDelayedReindexes = async () => {
};

const withTransaction = async <T>(
operation: (context: TransactionOperation) => Promise<T>
operation: (context: TransactionOperation) => Promise<T>,
namespace?: string
): Promise<T> => {
const logger = DefaultLogger();
const startTime = performance.now();

if (!tenants.current().featureFlags?.v1_transactions) {
return operation({ abort: async () => {} });
}

const logger = DefaultLogger();
const startTime = performance.now();
const logNamespace = namespace ? `(${namespace})` : '';

const session = await dbSessionContext.startSession();
session.startTransaction();
let wasManuallyAborted = false;
Expand All @@ -59,7 +62,7 @@ const withTransaction = async <T>(
wasManuallyAborted = true;
const elapsedTime = performance.now() - startTime;
logger.info(
`[v1_transactions] Transactions was manually aborted, session id -> ${inspect(session.id)} (${elapsedTime.toFixed(2)}ms)`
`[v1_transactions] Transactions ${logNamespace} was manually aborted, session id -> ${inspect(session.id)} (${elapsedTime.toFixed(2)}ms)`
);
},
};
Expand All @@ -68,7 +71,7 @@ const withTransaction = async <T>(
const result = await operation(context);
const operationTime = performance.now() - startTime;
logger.info(
`[v1_transactions] Transaction operations was successfully completed, session id -> ${inspect(session.id)} (${operationTime.toFixed(2)}ms)`
`[v1_transactions] Transaction ${logNamespace} operations was successfully completed, session id -> ${inspect(session.id)} (${operationTime.toFixed(2)}ms)`

Check failure on line 74 in app/api/utils/withTransaction.ts

View workflow job for this annotation

GitHub Actions / eslint

This line has a length of 161. Maximum allowed is 150
);

if (!wasManuallyAborted) {
Expand All @@ -77,29 +80,29 @@ const withTransaction = async <T>(
await performDelayedFileStores();
const fileStoresTime = performance.now() - beforeFileStores;
logger.info(
`[v1_transactions] Transaction saved all files before session commit, session id -> ${inspect(session.id)} (${fileStoresTime.toFixed(2)}ms)`
`[v1_transactions] Transaction ${logNamespace} saved all files before session commit, session id -> ${inspect(session.id)} (${fileStoresTime.toFixed(2)}ms)`

Check failure on line 83 in app/api/utils/withTransaction.ts

View workflow job for this annotation

GitHub Actions / eslint

This line has a length of 164. Maximum allowed is 150
);

const beforeCommit = performance.now();
await session.commitTransaction();
const commitTime = performance.now() - beforeCommit;
logger.info(
`[v1_transactions] Transaction session was commited, session id -> ${inspect(session.id)} (${commitTime.toFixed(2)}ms)`
`[v1_transactions] Transaction ${logNamespace} session was commited, session id -> ${inspect(session.id)} (${commitTime.toFixed(2)}ms)`
);

const beforeReindex = performance.now();
await performDelayedReindexes();
const reindexTime = performance.now() - beforeReindex;
logger.info(
`[v1_transactions] Transaction elasticsearch reindexes after session was commited, session id -> ${inspect(session.id)} (${reindexTime.toFixed(2)}ms)`
`[v1_transactions] Transaction ${logNamespace} elasticsearch reindexes after session was commited, session id -> ${inspect(session.id)} (${reindexTime.toFixed(2)}ms)`

Check failure on line 97 in app/api/utils/withTransaction.ts

View workflow job for this annotation

GitHub Actions / eslint

This line has a length of 174. Maximum allowed is 150
);
}
return result;
} catch (e) {
if (!wasManuallyAborted) {
const errorTime = performance.now() - startTime;
logger.info(
`[v1_transactions] Transaction aborted due to error: ${inspect(e)}, session id -> ${inspect(session.id)} (${errorTime.toFixed(2)}ms)`
`[v1_transactions] Transaction ${logNamespace} aborted due to error: ${inspect(e)}, session id -> ${inspect(session.id)} (${errorTime.toFixed(2)}ms)`

Check failure on line 105 in app/api/utils/withTransaction.ts

View workflow job for this annotation

GitHub Actions / eslint

This line has a length of 157. Maximum allowed is 150
);
await session.abortTransaction();
}
Expand All @@ -109,13 +112,13 @@ const withTransaction = async <T>(
dbSessionContext.clearContext();
const clearTime = performance.now() - beforeClearContext;
logger.info(
`[v1_transactions] Transaction cleared all context info, session id -> ${inspect(session.id)} (${clearTime.toFixed(2)}ms)`
`[v1_transactions] Transaction ${logNamespace} cleared all context info, session id -> ${inspect(session.id)} (${clearTime.toFixed(2)}ms)`
);
const beforeClearSession = performance.now();
await session.endSession();
const endSessionTime = performance.now() - beforeClearSession;
logger.info(
`[v1_transactions] Transaction session ended, session id -> ${inspect(session.id)} (${endSessionTime.toFixed(2)}ms)`
`[v1_transactions] Transaction ${logNamespace} session ended, session id -> ${inspect(session.id)} (${endSessionTime.toFixed(2)}ms)`
);
}
};
Expand Down

0 comments on commit cfba0df

Please sign in to comment.