Use the same DB transaction in different DAOs

380 Views Asked by At

I have a use case in one of my Golang applications to update multiple tables in an API and in case one of the updates fails, I would want all the previous updates to roll back (Something that @Transactional in Java does). I tried doing it in the below way:

func (d *dao) Method1(opt string) error {
    tx, err := d.DB.BeginTx(context.Background(), nil)
    if err != nil {
        return errors.Errorf("Unable to start transaction")
    }
    err := d.dao1.Update1(opt)
        if err != nil {
            log.Infof("Error : %s. Rolling back transaction", err)
            _ = tx.Rollback()
            log.Infof("Transaction rolled back while update 1")
            return err
        }
        err = d.dao2.Update2(opt)
        if err != nil {
            log.Errorf("Error in making update 2 "+
                "Error %v ", err)
            _ = tx.Rollback()
            log.Infof("Transaction rolled back while update 2")
            return err
        }
    }
}


func(d *dao1) Update1 error {
        var query = "update T1 set C1 = ? where id = ?"
        _, err := d.DB.Exec(query, "v1", "v2")
        return err
}

func(d *dao2) Update2 error {
        var query = "update T2 set C2 = ? where id = ?"
        _, err := d.DB.Exec(query, "v1", "v2")
        return err
}

But this doesn't work as we are creating a new transaction in the Update1 & Update2 methods. So I tried to pass transactions in the method parameters of method Update1 & Update2 like below:

func Update1(tx sql.Tx) error {
    var query = "update T1 set C1 = ? where id = ?"
    _, err := tx.Exec(query, "v1", "v2")
    return err
}

But the problem is in some use cases I want just method Update1 to be called. In that case, what should be the value of the transaction to be passed in the Update1 method?

Or is there a better way to do it. Can someone please help?

1

There are 1 best solutions below

1
On

If I understand correctly, in some cases you want Update1 to be called but not Update2, while in other cases you want both updates to happen. If that’s the case, then I would suggest that this is more of a design problem, rather than with the usage of Tx. I think designing two functions that each describes the behavior you expect, and delegating the logic about which one to call to the caller, might work. Something like:

func Caller() error {
    if condition {
        return UpdateOne()
    }
    return UpdateBoth()
}

func UpdateOne() error { … }
func UpdateBoth() error { … }