in src/dataServices/dynamoDbBundleService.ts [102:209]
async transaction(request: TransactionRequest): Promise<BundleResponse> {
this.assertValidTenancyMode(request.tenantId);
const { requests, startTime, tenantId } = request;
if (requests.length === 0) {
return {
success: true,
message: 'No requests to process',
batchReadWriteResponses: [],
};
}
// 1. Put a lock on all requests
const lockItemsResponse = await this.lockItems(requests, tenantId);
const { successfulLock } = lockItemsResponse;
let { lockedItems } = lockItemsResponse;
let elapsedTimeInMs = this.getElapsedTime(startTime);
if (elapsedTimeInMs > this.maxExecutionTimeMs || !successfulLock) {
await this.unlockItems(lockedItems, true);
if (elapsedTimeInMs > this.maxExecutionTimeMs) {
logger.warn(
'Locks were rolled back because elapsed time is longer than max code execution time. Elapsed time',
elapsedTimeInMs,
);
return {
success: false,
message: this.ELAPSED_TIME_WARNING_MESSAGE,
batchReadWriteResponses: [],
errorType: 'USER_ERROR',
};
}
logger.error('Locks were rolled back because failed to lock resources');
const { errorType, errorMessage } = lockItemsResponse;
return {
success: false,
message: errorMessage || 'Failed to lock resources for transaction',
batchReadWriteResponses: [],
errorType,
};
}
if (this.versionedLinks) {
const wasSuccessful = await this.updatedReferences(requests, lockedItems, tenantId);
elapsedTimeInMs = this.getElapsedTime(startTime);
if (elapsedTimeInMs > this.maxExecutionTimeMs || !wasSuccessful) {
await this.unlockItems(lockedItems, true, tenantId);
if (elapsedTimeInMs > this.maxExecutionTimeMs) {
logger.warn(
'Locks were rolled back because elapsed time is longer than max code execution time. Elapsed time',
elapsedTimeInMs,
);
return {
success: false,
message: this.ELAPSED_TIME_WARNING_MESSAGE,
batchReadWriteResponses: [],
errorType: 'USER_ERROR',
};
}
logger.error('Locks were rolled back because failed to find versions of some resources');
return {
success: false,
message: 'Failed to find some resource versions for transaction',
batchReadWriteResponses: [],
errorType: 'USER_ERROR',
};
}
}
// 2. Stage resources
const stageItemResponse = await this.stageItems(requests, lockedItems, tenantId);
const { batchReadWriteResponses } = stageItemResponse;
const successfullyStageItems = stageItemResponse.success;
lockedItems = stageItemResponse.lockedItems;
elapsedTimeInMs = this.getElapsedTime(startTime);
if (elapsedTimeInMs > this.maxExecutionTimeMs || !successfullyStageItems) {
lockedItems = await this.rollbackItems(batchReadWriteResponses, lockedItems, tenantId);
await this.unlockItems(lockedItems, true, tenantId);
if (elapsedTimeInMs > this.maxExecutionTimeMs) {
logger.warn(
'Rolled changes back because elapsed time is longer than max code execution time. Elapsed time',
elapsedTimeInMs,
);
return {
success: false,
message: this.ELAPSED_TIME_WARNING_MESSAGE,
batchReadWriteResponses: [],
errorType: 'USER_ERROR',
};
}
logger.error('Rolled changes back because staging of items failed');
return {
success: false,
message: 'Failed to stage resources for transaction',
batchReadWriteResponses: [],
errorType: 'SYSTEM_ERROR',
};
}
// 3. unlockItems
await this.unlockItems(lockedItems, false, tenantId);
return {
success: true,
message: 'Successfully committed requests to DB',
batchReadWriteResponses,
};
}