gocraft / dbr

Additions to Go's database/sql for super fast performance and convenience.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Helper function for transaction

manishtomar opened this issue · comments

Hello, Would you be willing to accept a helper function that executes a given function inside a transaction and automatically commits or rollbacks based on function return? This is how function will be used:

sess := conn.NewSession(nil)
return sess.WithTransaction(ctx, func(tx *dbr.Tx) error {
    if err = tx.Update(); err != nil {
        return err
    }
    ...
    return nil
})

WithTransaction starts a transaction and calls do with the transaction object. If do returns nil then transaction is committed, otherwise it is rolled back and original error occurring in do is returned. If returned error is special value ErrRollback then WithTransaction returns nil. This gives consumers to decide whether to rollback or not. I've currently implemented this internally and this is how it looks:

// withTransaction starts a transaction with given fields and executes the function within that transaction.
// Transaction is committed only if function returns nil. If it returns non-nil error then it is
// rolled back. However, if returned error is errRollback then withTransaction returns nil after
// successfully rolling back
func  withTransaction(ctx context.Context, do func(tx *dbr.Tx) error) error {
	sess := pg.conn.NewSession()
	tx, err := sess.BeginTx(ctx, nil)
	if err != nil {
		return err
	}
	err = do(tx)
	if err != nil {
		// function errored. rollback either way
		rberr := tx.Rollback()
		if rberr != nil && rberr != sql.ErrTxDone {
			// log the original function error since we will lose it here when wrapping rollback error
			logger.WithError(err).Error("Error from withTransaction function")
			return rberr
		}
		if err == errRollback {
			// function intended to rollback, hence return success after successfully rolling back
			return nil
		}
		// return original error after successfully rolling back
		return err
	}
	err = tx.Commit()
	if err != nil && err != sql.ErrTxDone {
		return err
	}
	return nil
}

LMK if this looks good and I'll create a PR with this. Thanks for a great library!

commented

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@taylorchu Can you please have a look at above recommendation? I'll be happy to open a PR if you are ok to review and merge it. Thanks!

yes. lgtm 👍

commented

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.