AとBの2つのサービスがあると仮定します。サービスAには次の機能があります。
ここで、次のステップ3または4のいずれかが失敗したと仮定します。サービスBがデータベースに変更を加えたため、それらの変更はまだ残っています。
この場合、データベースをロールバックする方法はありますか?私はデータベーストランザクションについて考えましたが、ネストjsでそれを行う方法を見つけることができませんでした。TypeOrmでサポートされていますが、ネストするのは自然ではありません。そうでない場合、サービスBによって行われた変更で「行き詰まっている」のですが、変更がなければAによって行われるはずでした。
どうもありがとう。
多くのソリューションが利用可能ですが、それらはすべてSQLトランザクション管理に基づいている必要があります。
個人的には、データベースでコードを実行するときに同じEntityManager
インスタンスを使用するのが最も簡単な方法だと思います。次に、次のようなものを使用できます。
getConnection().transaction(entityManager -> {
service1.doStuff1(entityManager);
service2.doStuff2(entityManager);
});
ORM操作の外部で生のSQLを実行する場合に備えて、同じトランザクションにラップされるQueryRunner
インスタンスからEntityManager
を生成できます。また、Repository
からEntityManager
インスタンスを生成する必要もあります。そうしないと、メイントランザクションの外部でコードが実行されます。
悲観的ロックを使用する必要があったので、ここでそれを解決しました。
NestJS
にTypeormのインスタンスを挿入するようにConnection
を要求するだけでよいので、これは「ネスト」的な方法だと思います。
@Injectable()
class MyService {
// 1. Inject the Typeorm Connection
constructor(@InjectConnection() private connection: Connection) { }
async findById(id: number): Promise<Thing> {
return new Promise(resolve => {
// 2. Do your business logic
this.connection.transaction(async entityManager => {
resolve(
await entityManager.findOne(Thing, id, {
lock: { mode: 'pessimistic_write' },
}),
);
});
});
}
}
必要な他のロジックを.transaction
ブロック内に配置するだけで問題ありません。
注:.transaction
メソッドによって提供されるentityManager
を使用する必要があります。そうしないと機能しません。
この場合、両方のデータベース操作に同じトランザクションマネージャを使用する必要があります。残念ながら、サンプルリポジトリはありませんが、ノードで継続ローカルストレージ(CLS)を使用する潜在的なソリューションを見つけました。
https://github.com/typeorm/typeorm/issues/1895
これはExpress.jsにも当てはまりますが、TransactionManagerのインスタンスを(たとえば、ネストミドルウェアに)作成して、リクエストコンテキストごとに保存できます。その後、上記のリンクで@Transactionデコレーター実装のアノテーションが付けられている場合、サービスメソッド呼び出し全体でこのトランザクションマネージャーを再利用できます。
関数チェーンにエラーがない場合、トランザクションマネージャは行われたすべての変更をコミットします。それ以外の場合、マネージャーは変更をロールバックします。
お役に立てれば!