Я столкнулся с противоречивым поведением при работе с pg_locks
в подпрограммах go и PostgreSQL 9.5.
Когда я создаю дополнительный goroutines
и вызываю SELECT pg_try_advisory_lock(1);
, результаты противоречивы.
Я могу попытаться получить блокировку в какой-то момент, потерпеть неудачу, попробовать еще раз и получить ее, пока кто-нибудь явно не снимет блокировку.
Я создал небольшую программу для воспроизведения проблемы.
Программный поток
- Создать 10 горутин. Каждый из них пытается получить одинаковую блокировку при инициализации.
- Каждую секунду каждый экземпляр будет пытаться снова получить блокировку, если он еще не получил ее.
- Каждую секунду я проверяю все экземпляры и подсчитываю, сколько уже получило блокировку.
Ожидаемое поведение:
Только 1 программа была бы заблокирована в любой момент.
Фактические результаты:
Число горутинов, которым удается захватить замок, со временем увеличивается.
package main
var dbMap *gorp.DbMap // init code omitted for brevity
func main() {
setup(10)
fmt.Println("after initialization,", countOwners(), "instances of lockOwners have the lock!")
for {
if _, err := dbMap.Exec("SELECT pg_sleep(1)"); err != nil {
panic(err)
}
fmt.Println(countOwners(), "instances of lockOwners have the lock!")
}
}
func countOwners() int {
possessLock := 0
for _, lo := range los {
if lo.hasLock {
possessLock++
}
}
return possessLock
}
var los []*lockOwner
func setup(instanceCount int) {
var wg sync.WaitGroup
for i := 0; i < instanceCount; i++ {
wg.Add(1)
newInstance := lockOwner{}
los = append(los, &newInstance)
go newInstance.begin(time.Second, &wg, i+1)
}
wg.Wait()
}
type lockOwner struct {
id int
ticker *time.Ticker
hasLock bool
}
func (lo *lockOwner) begin(interval time.Duration, wg *sync.WaitGroup, id int) {
lo.ticker = time.NewTicker(interval)
lo.id = id
go func() {
lo.tryToGetLock()
wg.Done()
for range lo.ticker.C {
lo.tryToGetLock()
}
}()
}
func (lo *lockOwner) tryToGetLock() {
if lo.hasLock {
return
}
locked, err := dbMap.SelectStr("SELECT pg_try_advisory_lock(4);")
if err != nil {
panic(err)
}
if locked == "true" {
fmt.Println(lo.id, "Did get lock!")
lo.hasLock = true
}
}
Вывод этой программы варьируется, но обычно что-то похожее:
1 Did get lock!
after initialization, 1 instances of lockOwners have the lock!
1 instances of lockOwners have the lock!
2 Did get lock!
2 instances of lockOwners have the lock!
2 instances of lockOwners have the lock!
7 Did get lock!
3 instances of lockOwners have the lock!
3 instances of lockOwners have the lock!
6 Did get lock!
4 instances of lockOwners have the lock!
Мой вопрос:
- Какую защиту следует ожидать при использовании
pg_locks
таким образом?
- По какой причине некоторые программы не могут получить блокировку?
По какой причине та же самая программа удачно делает это при следующей попытке?
- Может быть, поток заблокирован ресурсом, и каждый раз, когда
goroutine
запускает его, это происходит из другого потока? Это объясняет противоречивое поведение.