refactor: share tbank-sync claim and generation lock (WealthLedger/backend#166) (#188)
Backend verify / commits (push) Skipped
Backend verify / security (push) Successful in 1m9s
Backend verify / verify (push) Failing after 7m40s

This commit was merged in pull request #188.
This commit is contained in:
2026-11-04 20:47:50 +03:00
parent c5ccfeadea
commit 5d31b3e6dd
8 changed files with 167 additions and 206 deletions
@@ -7,13 +7,14 @@ import { createHash, randomUUID } from 'node:crypto';
import { isDeepStrictEqual } from 'node:util';
import type { Prisma } from '../../../../../generated/prisma/client';
import type {
TbankOperationItem,
TbankOperationsByCursorResponse,
} from '../../../../integrations/tbank/tbank.api';
import { TbankErrorCode } from '../../../../integrations/tbank/tbank-error-code';
import { isTbankProtocolValue, TbankOperationState } from '../../../../integrations/tbank/tbank-protocol';
import { claimSyncWork, lockConnectionGeneration } from '../../../../integrations/tbank/tbank-sync-claim';
import {
isJoinableRunningAttempt,
type TbankOperationItem, type TbankOperationsByCursorResponse,
} from '../../../../integrations/tbank/tbank.api';
import { PrismaService } from '../../../../prisma/prisma.service';
import {
dateOnlyBounds,
@@ -268,16 +269,10 @@ export class BrokerageOperationsRepository {
.update(JSON.stringify([...new Set(intervals.map(intervalKey))].sort()))
.digest('hex');
return this.prisma.$transaction(async (tx) => {
const locked = await tx.tbankConnection.updateMany({
where: {
userId,
revision,
invalidToken: false,
membershipAt: { not: null },
},
data: { membershipRevision: { increment: 0 } },
const locked = await lockConnectionGeneration(tx, { userId, revision }, {
membershipAt: { not: null },
});
if (!locked.count) return null;
if (!locked) return null;
const accounts = await tx.tbankAccount.findMany({
where: { userId, revision },
select: { accountId: true, openedOn: true },
@@ -379,9 +374,7 @@ export class BrokerageOperationsRepository {
publicationAtomic && previousIntent && !previousIntent.atomic;
if (
touching.length <= SINGLE_WORK_GROUP &&
previous?.state === 'running' &&
previous.processRunId === processRunId &&
runningAttempts.has(previous.attemptId) &&
isJoinableRunningAttempt(previous, processRunId, runningAttempts) &&
!upgradeGroup
) {
const pending = previousIntent?.intervals.map(decodeInterval) ?? [];
@@ -933,15 +926,8 @@ async function claim(
tx: Prisma.TransactionClient,
attempt: OperationAttempt,
): Promise<boolean> {
const result = await tx.tbankSyncWork.updateMany({
where: {
...attempt,
state: 'running',
connection: { invalidToken: false },
},
data: { updatedAt: new Date() },
});
return result.count === EXPECTED_UPDATED_WORK_ITEMS;
const count = await claimSyncWork(tx, attempt, { updatedAt: new Date() });
return count === EXPECTED_UPDATED_WORK_ITEMS;
}
function decodeInterval(key: string): OperationInterval {