diff --git a/backend/src/graphql/resolver/TransactionLinkResolver.ts b/backend/src/graphql/resolver/TransactionLinkResolver.ts index 3756d3bd9..869104bae 100644 --- a/backend/src/graphql/resolver/TransactionLinkResolver.ts +++ b/backend/src/graphql/resolver/TransactionLinkResolver.ts @@ -396,8 +396,8 @@ export class TransactionLinkResolver { } return true } else { + const releaseLinkLock = await TRANSACTION_LINK_LOCK.acquire() const now = new Date() - const releaseLinkLock = await TRANSACTION_LINK_LOCK.acquire() try { const transactionLink = await DbTransactionLink.findOne({ where: { code } }) if (!transactionLink) { diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index 505438f6a..0bd77166c 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -58,6 +58,8 @@ export const executeTransaction = async ( logger: Logger, transactionLink?: dbTransactionLink | null, ): Promise => { + // acquire lock + const releaseLock = await TRANSACTIONS_LOCK.acquire() const receivedCallDate = new Date() let dltTransactionPromise: Promise = Promise.resolve(null) if (!transactionLink) { @@ -66,9 +68,6 @@ export const executeTransaction = async ( dltTransactionPromise = redeemDeferredTransferTransaction(transactionLink, amount.toString(), receivedCallDate, recipient) } - // acquire lock - const releaseLock = await TRANSACTIONS_LOCK.acquire() - try { logger.info('executeTransaction', memo) diff --git a/backend/src/graphql/resolver/semaphore.test.ts b/backend/src/graphql/resolver/semaphore.test.ts index 9b4ec07df..d0bf08b7c 100644 --- a/backend/src/graphql/resolver/semaphore.test.ts +++ b/backend/src/graphql/resolver/semaphore.test.ts @@ -22,10 +22,12 @@ import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg' import { bobBaumeister } from '@/seeds/users/bob-baumeister' import { peterLustig } from '@/seeds/users/peter-lustig' import { CONFIG } from '@/config' +import { TRANSACTIONS_LOCK } from 'database' jest.mock('@/password/EncryptorUtils') CONFIG.DLT_CONNECTOR = false +CONFIG.EMAIL = false let mutate: ApolloServerTestClient['mutate'] let con: DataSource @@ -47,7 +49,43 @@ afterAll(async () => { await con.destroy() }) +type RunOrder = { [key: number]: { start: number, end: number } } +async function fakeWork(runOrder: RunOrder, index: number) { + const releaseLock = await TRANSACTIONS_LOCK.acquire() + const startDate = new Date() + await new Promise((resolve) => setTimeout(resolve, Math.random() * 50)) + const endDate = new Date() + runOrder[index] = { start: startDate.getTime(), end: endDate.getTime() } + releaseLock() +} + describe('semaphore', () => { + it("didn't should run in parallel", async () => { + const runOrder: RunOrder = {} + await Promise.all([ + fakeWork(runOrder, 1), + fakeWork(runOrder, 2), + fakeWork(runOrder, 3), + fakeWork(runOrder, 4), + fakeWork(runOrder, 5), + ]) + expect(runOrder[1].start).toBeLessThan(runOrder[1].end) + expect(runOrder[1].start).toBeLessThan(runOrder[2].start) + expect(runOrder[2].start).toBeLessThan(runOrder[2].end) + expect(runOrder[2].start).toBeLessThan(runOrder[3].start) + expect(runOrder[3].start).toBeLessThan(runOrder[3].end) + expect(runOrder[3].start).toBeLessThan(runOrder[4].start) + expect(runOrder[4].start).toBeLessThan(runOrder[4].end) + expect(runOrder[4].start).toBeLessThan(runOrder[5].start) + expect(runOrder[5].start).toBeLessThan(runOrder[5].end) + expect(runOrder[1].end).toBeLessThan(runOrder[2].end) + expect(runOrder[2].end).toBeLessThan(runOrder[3].end) + expect(runOrder[3].end).toBeLessThan(runOrder[4].end) + expect(runOrder[4].end).toBeLessThan(runOrder[5].end) + }) +}) + +describe('semaphore fullstack', () => { let contributionLinkCode = '' let bobsTransactionLinkCode = '' let bibisTransactionLinkCode = ''