go-pg/pg

SelectOrInsert is not safe for transactions

tbarbugli opened this issue · 0 comments

Say that you have something like this:

db.RunInTransaction(ctx, func(tx *pg.Tx) error {
   inserted, err := tx.Model(). SelectOrInsert()
})

if this code runs concurrently you will get a transaction aborted error instead of a nil error. This happens because SelectOrInsert does this inside of a retry loop:

if err != nil {
	insertErr = err
	if err == internal.ErrNoRows {
		continue
	}
	if pgErr, ok := err.(internal.PGError); ok {
		if pgErr.IntegrityViolation() {
			continue
		}
		if pgErr.Field('C') == "55000" {
			// Retry on "#55000 attempted to delete invisible tuple".
			continue
		}
	}
	return false, err
}

which is OK unless you are doing this inside a transaction, the moment you hit an error the transaction will be aborted and the next SELECT will error.