Files

38 lines
1.6 KiB
Go

//go:build ignore
package gorules
import "github.com/quasilyte/go-ruleguard/dsl"
// nestedDBTransaction flags a DataStore transaction started from inside another
// transaction closure. bbolt only allows one open write transaction at a time,
// so starting a second one while the first is still open deadlocks. This fires
// regardless of whether the inner transaction is opened through the same store
// variable or a different one, since there is only one underlying database.
func nestedDBTransaction(m dsl.Matcher) {
m.Match(
`$store.UpdateTx($fn)`,
`$store.UpdateTxLowPriority($fn)`,
`$store.ViewTx($fn)`,
).
Where(
m["fn"].Contains(`$_.UpdateTx($_)`) ||
m["fn"].Contains(`$_.UpdateTxLowPriority($_)`) ||
m["fn"].Contains(`$_.ViewTx($_)`)).
Report(`$fn starts another transaction while already running inside $store's transaction; only one write transaction can be open at a time, so this can deadlock`)
}
// dataStoreCallInsideTx flags a service accessed through the DataStore instead
// of the tx handle inside a transaction closure. The non-tx CRUD methods open
// their own transaction internally (see BaseDataService), so calling them from
// inside an already-open transaction is a nested transaction in disguise.
func dataStoreCallInsideTx(m dsl.Matcher) {
m.Match(
`$store.UpdateTx($fn)`,
`$store.UpdateTxLowPriority($fn)`,
`$store.ViewTx($fn)`,
).
Where(m["fn"].Contains(`$store.$service().$method($*_)`)).
Report(`$service() is accessed through $store instead of the tx parameter inside this closure; the non-tx call starts its own transaction internally and can deadlock`)
}