En supposant que nous ayons 2 services, A et B. Le service A a une fonction faisant ce qui suit:
Supposons maintenant que l'une des étapes suivantes, les étapes 3 ou 4, a échoué. Étant donné que le service B a apporté des modifications à la base de données, ces modifications sont toujours là.
Existe-t-il un moyen de restaurer la base de données dans ce cas? J'ai pensé aux transactions de base de données, mais je n'ai trouvé aucun moyen de le faire dans nest js, bien qu'il soit pris en charge par TypeOrm, il ne semble pas naturel d'imbriquer. Sinon, je suis maintenant "bloqué" avec les changements survenus par le service B, mais sans les changements auraient dû se produire par A.
Merci beaucoup.
De nombreuses solutions sont disponibles, elles devraient toutes être basées sur la gestion des transactions SQL.
Personnellement, je pense que la façon la plus simple d'y parvenir est d'utiliser la même instance EntityManager
lorsque vous exécutez du code sur votre base de données. Ensuite, vous pouvez utiliser quelque chose comme:
getConnection().transaction(entityManager -> {
service1.doStuff1(entityManager);
service2.doStuff2(entityManager);
});
Vous pouvez générer une QueryRunner
à partir d'une instance EntityManager
qui sera encapsulée dans la même transaction si vous exécutez du SQL brut en dehors des opérations ORM. Vous devez également générer Repository
instances à partir de EntityManager
ou ils exécuteront du code en dehors de la transaction principale.
Voici comment je l'ai résolu car j'avais besoin d'utiliser un verrou pessimiste.
Je pense que c'est la façon "Nest" de faire les choses car vous pouvez simplement demander à NestJS
d'injecter une instance d'un Typeorm Connection
et vous êtes prêt à partir.
@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' },
}),
);
});
});
}
}
Placez simplement toute autre logique dont vous avez besoin dans le .transaction
bloquer et vous êtes prêt à partir.
REMARQUE: Vous DEVEZ utiliser le entityManager
fourni par le .transaction
méthode sinon cela ne fonctionnera pas.
Dans ce cas, vous devez utiliser le même gestionnaire de transactions pour les deux opérations de base de données. Malheureusement, je n'ai pas d'exemple de référentiel, mais j'ai trouvé une solution potentielle en utilisant Continuation Local Storage (CLS) dans Node:
https://github.com/typeorm/typeorm/issues/1895
Cela s'applique à Express.js, mais vous pouvez créer une instance de TransactionManager (par exemple, dans un middleware imbriqué) et la stocker pour chaque contexte de demande. Vous pourrez ensuite réutiliser ce gestionnaire transactionnel dans vos appels de méthode de service, à condition qu'ils soient annotés avec l'implémentation de décorateur @Transaction dans le lien ci-dessus.
S'il n'y a aucune erreur dans votre chaîne de fonctions, le gestionnaire de transactions valide toutes les modifications apportées. Sinon, le gestionnaire annulera toutes les modifications.
J'espère que cela t'aides!