Skip to content

Commit

Permalink
refactor(server): use @nestjs-cls/transactional to impl database tran…
Browse files Browse the repository at this point in the history
…saction
  • Loading branch information
fengmk2 committed Jan 17, 2025
1 parent 77869f1 commit 7fac2ec
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 27 deletions.
2 changes: 2 additions & 0 deletions packages/backend/server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
"@google-cloud/opentelemetry-cloud-monitoring-exporter": "^0.20.0",
"@google-cloud/opentelemetry-cloud-trace-exporter": "^2.4.1",
"@google-cloud/opentelemetry-resource-util": "^2.4.0",
"@nestjs-cls/transactional": "^2.4.4",
"@nestjs-cls/transactional-adapter-prisma": "^1.2.7",
"@nestjs/apollo": "^12.2.2",
"@nestjs/common": "^10.4.15",
"@nestjs/core": "^10.4.15",
Expand Down
14 changes: 14 additions & 0 deletions packages/backend/server/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import {
Module,
} from '@nestjs/common';
import { ScheduleModule } from '@nestjs/schedule';
import { ClsPluginTransactional } from '@nestjs-cls/transactional';
import { TransactionalAdapterPrisma } from '@nestjs-cls/transactional-adapter-prisma';
import { PrismaClient } from '@prisma/client';
import type { Request } from 'express';
import { get } from 'lodash-es';
import { ClsModule } from 'nestjs-cls';
Expand Down Expand Up @@ -55,6 +58,17 @@ export const FunctionalityModules = [
return (req.headers['x-request-id'] as string) ?? randomUUID();
},
},
plugins: [
// https://papooch.github.io/nestjs-cls/plugins/available-plugins/transactional/prisma-adapter
new ClsPluginTransactional({
// if PrismaModule is not global, we need to make it available to the plugin
// imports: [ PrismaModule ],
adapter: new TransactionalAdapterPrisma({
// each adapter has its own options, see the specific adapter docs for details
prismaInjectionToken: PrismaClient,
}),
}),
],
}),
ConfigModule.forRoot(),
RuntimeModule,
Expand Down
9 changes: 9 additions & 0 deletions packages/backend/server/src/models/base.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { Inject } from '@nestjs/common';
import { TransactionHost } from '@nestjs-cls/transactional';
import type { TransactionalAdapterPrisma } from '@nestjs-cls/transactional-adapter-prisma';
import { PrismaClient } from '@prisma/client';

import { AFFiNELogger, Config } from '../base';
Expand All @@ -16,4 +18,11 @@ export class BaseModel {

@Inject(PrismaClient)
protected readonly db!: PrismaClient;

@Inject(TransactionHost)
private readonly txHost!: TransactionHost<TransactionalAdapterPrisma>;

protected get tx() {
return this.txHost.tx;
}
}
51 changes: 24 additions & 27 deletions packages/backend/server/src/models/feature.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Injectable } from '@nestjs/common';
import { Transactional } from '@nestjs-cls/transactional';
import { Feature } from '@prisma/client';
import { z } from 'zod';

import { PrismaTransaction } from '../base';
import { BaseModel } from './base';
import { Features, FeatureType } from './common';

Expand All @@ -20,7 +20,7 @@ type FeatureConfigs<T extends FeatureNames> = z.infer<
@Injectable()
export class FeatureModel extends BaseModel {
async get<T extends FeatureNames>(name: T) {
const feature = await this.getLatest(this.db, name);
const feature = await this.getLatest(name);

// All features are hardcoded in the codebase
// It would be a fatal error if the feature is not found in DB.
Expand All @@ -43,6 +43,7 @@ export class FeatureModel extends BaseModel {
};
}

@Transactional()
async upsert<T extends FeatureNames>(name: T, configs: FeatureConfigs<T>) {
const shape = this.getConfigShape(name);
const parseResult = shape.safeParse(configs);
Expand All @@ -58,37 +59,33 @@ export class FeatureModel extends BaseModel {
// TODO(@forehalo):
// could be a simple upsert operation, but we got useless `version` column in the database
// will be fixed when `version` column gets deprecated
const feature = await this.db.$transaction(async tx => {
const latest = await this.getLatest(tx, name);

if (!latest) {
return await tx.feature.create({
data: {
type: FeatureType.Feature,
feature: name,
configs: parsedConfigs,
},
});
} else {
return await tx.feature.update({
where: { id: latest.id },
data: {
configs: parsedConfigs,
},
});
}
});
const latest = await this.getLatest(name);

let feature: Feature;
if (!latest) {
feature = await this.tx.feature.create({
data: {
type: FeatureType.Feature,
feature: name,
configs: parsedConfigs,
},
});
} else {
feature = await this.tx.feature.update({
where: { id: latest.id },
data: {
configs: parsedConfigs,
},
});
}

this.logger.verbose(`Feature ${name} upserted`);

return feature as Feature & { configs: FeatureConfigs<T> };
}

private async getLatest<T extends FeatureNames>(
client: PrismaTransaction,
name: T
) {
return client.feature.findFirst({
private async getLatest<T extends FeatureNames>(name: T) {
return this.tx.feature.findFirst({
where: { feature: name },
orderBy: { version: 'desc' },
});
Expand Down
27 changes: 27 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -778,6 +778,8 @@ __metadata:
"@google-cloud/opentelemetry-cloud-monitoring-exporter": "npm:^0.20.0"
"@google-cloud/opentelemetry-cloud-trace-exporter": "npm:^2.4.1"
"@google-cloud/opentelemetry-resource-util": "npm:^2.4.0"
"@nestjs-cls/transactional": "npm:^2.4.4"
"@nestjs-cls/transactional-adapter-prisma": "npm:^1.2.7"
"@nestjs/apollo": "npm:^12.2.2"
"@nestjs/common": "npm:^10.4.15"
"@nestjs/core": "npm:^10.4.15"
Expand Down Expand Up @@ -8806,6 +8808,31 @@ __metadata:
languageName: node
linkType: hard

"@nestjs-cls/transactional-adapter-prisma@npm:^1.2.7":
version: 1.2.7
resolution: "@nestjs-cls/transactional-adapter-prisma@npm:1.2.7"
peerDependencies:
"@nestjs-cls/transactional": ^2.4.4
"@prisma/client": "> 4 < 7"
nestjs-cls: ^4.5.0
prisma: "> 4 < 7"
checksum: 10/43472579bd872252c0de919106a931ed563d4359d5d775c989016cab948fcff42fcf62f1260d4459047ebb3d41e3677dddf566376dbaec3e93282be1fa33d2c1
languageName: node
linkType: hard

"@nestjs-cls/transactional@npm:^2.4.4":
version: 2.4.4
resolution: "@nestjs-cls/transactional@npm:2.4.4"
peerDependencies:
"@nestjs/common": "> 7.0.0 < 11"
"@nestjs/core": "> 7.0.0 < 11"
nestjs-cls: ^4.5.0
reflect-metadata: "*"
rxjs: ">= 7"
checksum: 10/1ec34e5b0ed8d9f1b781ebab56d616d775f038c5df21138836c61a524b6fec73ff0994d165edf5a54539ca87d5013b64fd4187b1a95bb75ba26252541c7164d0
languageName: node
linkType: hard

"@nestjs/apollo@npm:^12.2.2":
version: 12.2.2
resolution: "@nestjs/apollo@npm:12.2.2"
Expand Down

0 comments on commit 7fac2ec

Please sign in to comment.