refactor: share tbank-sync claim and generation lock (WealthLedger/backend#166)

This commit is contained in:
2026-11-04 17:59:35 +03:00
parent 02e0d560c8
commit 812b989130
8 changed files with 171 additions and 206 deletions
+14 -27
View File
@@ -12,6 +12,7 @@ import { PrismaService } from '../../prisma/prisma.service';
import { supportedTbankAccounts } from './tbank-account.policy';
import { TbankErrorCode } from './tbank-error-code';
import { tbankSourceNeedsRefresh } from './tbank-source.policy';
import { claimSyncWork, lockConnectionGeneration } from './tbank-sync-claim';
import { TbankTokenService } from './tbank-token.service';
const EXPECTED_UPDATED_CONNECTIONS = 1;
@@ -56,11 +57,8 @@ export class TbankAccessService {
force: boolean,
) {
return this.prisma.$transaction(async (tx) => {
const current = await tx.tbankConnection.updateMany({
where: { userId, revision, invalidToken: false },
data: { membershipRevision: { increment: 0 } },
});
if (!current.count) return null;
const current = await lockConnectionGeneration(tx, { userId, revision });
if (!current) return null;
const connection = await tx.tbankConnection.findUniqueOrThrow({
where: { userId },
});
@@ -110,15 +108,11 @@ export class TbankAccessService {
async failMembership(attempt: TbankSyncAttempt, invalidToken: boolean) {
if (invalidToken) return this.rejectMembershipToken(attempt);
const result = await this.prisma.tbankSyncWork.updateMany({
where: {
...attempt,
state: 'running',
connection: { invalidToken: false },
},
data: { state: 'failed', errorCode: TbankErrorCode.Unavailable },
const result = await claimSyncWork(this.prisma, attempt, {
state: 'failed',
errorCode: TbankErrorCode.Unavailable,
});
return result.count === EXPECTED_UPDATED_CONNECTIONS;
return result === EXPECTED_UPDATED_CONNECTIONS;
}
async tokenForAttempt(attempt: TbankSyncAttempt): Promise<null | string> {
@@ -163,15 +157,8 @@ export class TbankAccessService {
return this.prisma.$transaction(async (tx) => {
// Acquire the write fence before reading membership. Replacement and
// attempt supersession cannot interleave with the publication below.
const claimed = await tx.tbankSyncWork.updateMany({
where: {
...attempt,
state: 'running',
connection: { invalidToken: false },
},
data: { updatedAt: new Date() },
});
if (!claimed.count) return false;
const claimed = await claimSyncWork(tx, attempt, { updatedAt: new Date() });
if (!claimed) return false;
const { userId, revision } = attempt;
const previous = await tx.tbankAccount.findMany({
where: { userId, revision },
@@ -244,11 +231,11 @@ export class TbankAccessService {
async rejectMembershipToken(attempt: TbankSyncAttempt): Promise<boolean> {
return this.prisma.$transaction(async (tx) => {
const claimed = await tx.tbankSyncWork.updateMany({
where: { ...attempt, state: 'running' },
data: { state: 'failed', errorCode: TbankErrorCode.InvalidToken },
});
if (!claimed.count) return false;
const claimed = await claimSyncWork(tx, attempt, {
state: 'failed',
errorCode: TbankErrorCode.InvalidToken,
}, { withoutConnectionFence: true });
if (!claimed) return false;
await tx.tbankConnection.update({
where: {
userId_revision: {