Sequelize: различная обработка транзакций

#sql #node.js #tsql #sequelize.js

#sql #node.js #tsql #sequelize.js

Вопрос:

Допустим, мы внедрили пару (фиктивных) сервисов, использующих модели Sequelize для доступа к нашей БД:

 export class AService {
    // ... some other stuff
    async updateSomeA(aId: number, value: string) {

        let instanceA = await ModelA.findById(aId);

        // need this update to use the transaction!
        await instanceA.update({
            some_field: value
        });
    }

}

export class BService {
    // ... some other stuff
    async updateB(bId: number, value: string) {

        let instanceB = await ModelB.findById(bId);

        // and this one as well!
        await instanceB.update({
            some_field_in_b: value
        });
    }
}
  

Теперь мы хотим вызвать updateSomeA и updateB из другого места в нашем коде, скажем, из CService , но мы хотим, чтобы эти вызовы были включены в транзакцию.

 class CService {

    // ...stuff

    async f() {

        // == begin transaction
        await AService.updateSomeA(1, 'blah!');
        await BService.updateB(3, 'meh');
        // == end transaction
    }
}
  

Из того, что я собираю, просматривая документы, чтобы защитить код с помощью транзакции, мне нужно либо обернуть его в обратный вызов, либо использовать обещание. Это правильно?

Если это так, то я был бы вынужден модифицировать свои сервисы и, вероятно, иметь некоторые «транзакционные» версии моих методов, правильно?

Знаете ли вы, ребята, есть ли планы поддержки чего-то вроде

 Transaction.begin("myAwesomeTransaction");

// ... every SQL generated in here is 'safe'

Transaction.end();
  

РЕДАКТИРОВАТЬ: решение «транзакция как аргумент»

Конечно, мы всегда можем использовать транзакцию в качестве необязательного последнего аргумента во всех наших сервисных функциях или, по крайней мере, в тех, которые имеют побочные эффекты (например deleteA , updateB , и т.д.). Что-то вроде:

 // ... changed AService.updateSomeA
async updateSomeA(aId: number, value: string, t?: Sequelize.Transaction) {

    let instanceA = await ModelA.findById(aId);
    await instanceA.update({
        some_field: value
    }, {
        transaction: t  // we use the transaction if we get one
    });
}
  

Но что происходит с нашим CService.f() ? Должна ли эта функция создавать транзакцию и передавать ее или ожидать транзакцию в качестве необязательного аргумента? Ну, ответ и то, и другое, не так ли? Если мы вызываем f() из какой-либо другой службы как часть более крупной транзакции, мы ожидаем, что она получит этот Transaction экземпляр и будет использовать его. С другой стороны, если мы вызываем f() извне (например, контроллер), мы не хотим создавать транзакции там … верно ?!

Итак, мы получаем что-то вроде:

 // ... changed CService.f
async f(t?: Sequelize.Transaction) {

    if(!t) return sequelize.transaction(_f); // using the autoCallback overload
    else return _f(t);

    async function _f(trans: Sequelize.Transaction) {

        // ... actual code for f() goes here
    }
}
  

Идеально? Нет… С нетерпением ждем вашего вклада!

Комментарии:

1. Будет ли работать создание транзакции и добавление ее в сервисы? Поэтому вызывайте update транзакцию, если аргумент транзакции существует.

2. @user3254198 Да, добавление дополнительного необязательного аргумента к каждой функции было именно тем, чего я пытался избежать, но…

3. Транзакции в sequelize разрешаются с promises помощью . Так что действительно, передача транзакции была бы правильным решением. Вы не хотите создавать эту транзакцию в своем контроллере, но когда она вам действительно понадобится. Не до и, конечно, не после. Но то, как вы хотите, чтобы это работало (например, определена глобальная транзакция, и это также запускает другие файлы), не будет работать с sequelize