Как откатить транзакцию при сбое задачи в транзакции с помощью pg-обещания - PullRequest
3 голосов
/ 22 апреля 2020

Допустим, у меня есть две функции:

export const setThingAlertsInactive = (userID, thingIDs) => {
    return db.any(' UPDATE thing_alerts SET is_active = false WHERE IN (Select * from thing_alerts where user_id = $1 and thing_id IN ($2:csv))', [userID.toString(), thingIDs])
}

export const archiveOrRestoreThings = (thingIDs, archive) => {
    let archivedStatement =''
    if(archive === true){
        archivedStatement = 'archived = current_timestamp'
    } else if(archive === false){
        archivedStatement = 'archived = NULL'
    }
    return db.none(`UPDATE things SET ${archivedStatement} WHERE id IN ($1:csv)`, [thingIDs])
}

Я хочу запустить их вместе, поэтому, если одна из них выйдет из строя, другая откатится назад. Фактически я сознательно оставил ошибку в первом SQL Запросе.

Вот моя функция tx:

export const archiveOrRestoreThingsAndSetAlert = (userID, thingsIDs, archive) => {
    return db.tx((transaction) => {
        const queries = [archiveOrRestoreThings(thingIDs, archive), setThingAlertsInactive(userID, projectIDs)]
        return transaction.batch(queries)
    })
}

Первый запрос выполняется и работает. Второе терпит неудачу. Я должен быть в состоянии откатить их обратно в этом случае. Спасибо!

Ответы [ 2 ]

2 голосов
/ 23 апреля 2020

От автора pg-promise.


Причина, по которой он не работает для вас, заключается в том, что две функции запроса используют контекст соединения с базой данных root, а не контекст транзакции, то есть вы выполняете запросы вне подключения к транзакции.

Вы можете изменить их для поддержки необязательного контекста задачи / транзакции:

export const setThingAlertsInactive = (userID, thingIDs, t) => {
    return (t || db).none(`UPDATE thing_alerts SET is_active = false WHERE
           IN (Select * from thing_alerts where user_id = $1 and thing_id IN ($2:csv))`,
           [userID.toString(), thingIDs]);
}

export const archiveOrRestoreThings = (thingIDs, archive, t) => {
    let archivedStatement =''
    if(archive === true){
        archivedStatement = 'archived = current_timestamp'
    } else if(archive === false) {
        archivedStatement = 'archived = NULL'
    }
    return (t || db).none(`UPDATE things SET ${archivedStatement} WHERE id IN ($1:csv)`, 
                          [thingIDs]);
}

И нет смысла использовать batch, что это устаревший метод, необходимый только в особых случаях:

export const archiveOrRestoreThingsAndSetAlert = (userID, thingsIDs, archive) => {
    return db.tx(async t => {
        await archiveOrRestoreThings(thingIDs, archive, t);
        await setThingAlertsInactive(userID, projectIDs, t);
    })
}
1 голос
/ 23 апреля 2020

Вам нужно передать транзакцию в archiveOrRestoreThings и setThingAlertsInactive и вызвать .none и .any для транзакции вместо db. См. пример кода для справки.

...