Live video on the AT Protocol
at eli/revert-dev-env 73 lines 1.8 kB view raw
1package statedb 2 3import ( 4 "crypto/sha256" 5 "encoding/binary" 6 "fmt" 7 "sync" 8) 9 10func (state *StatefulDB) GetNamedLock(name string) (func(), error) { 11 // temp while we debug postgres 12 return state.getNamedLockSQLite(name) 13 // switch state.Type { 14 // case DBTypeSQLite: 15 // return state.getNamedLockSQLite(name) 16 // case DBTypePostgres: 17 // return state.getNamedLockPostgres(name) 18 // } 19 // panic("unsupported database type") 20} 21 22func (state *StatefulDB) getNamedLockPostgres(name string) (func(), error) { 23 // Convert string to sha256 hash and use decimal value for advisory lock 24 h := sha256.Sum256([]byte(name)) 25 nameInt := int64(binary.BigEndian.Uint64(h[:8])) 26 27 err := state.DB.Exec("SELECT pg_advisory_lock($1)", nameInt).Error 28 if err != nil { 29 return nil, err 30 } 31 return func() { 32 err := state.DB.Exec("SELECT pg_advisory_unlock($1)", nameInt).Error 33 if err != nil { 34 // unfortunate, but the risk is that we're holding on to the lock forever, 35 // so it's responsible to crash in this case 36 panic(fmt.Errorf("error unlocking named lock: %w", err)) 37 } 38 }, nil 39} 40 41func (state *StatefulDB) getNamedLockSQLite(name string) (func(), error) { 42 lock := state.locks.GetLock(name) 43 lock.Lock() 44 return func() { 45 lock.Unlock() 46 }, nil 47} 48 49// Local mutex implementation for sqlite 50type NamedLocks struct { 51 mu sync.Mutex 52 locks map[string]*sync.Mutex 53} 54 55// NewNamedLocks creates a new NamedLocks instance 56func NewNamedLocks() *NamedLocks { 57 return &NamedLocks{ 58 locks: make(map[string]*sync.Mutex), 59 } 60} 61 62// GetLock returns the mutex for the given name, creating it if it doesn't exist 63func (n *NamedLocks) GetLock(name string) *sync.Mutex { 64 n.mu.Lock() 65 defer n.mu.Unlock() 66 67 lock, exists := n.locks[name] 68 if !exists { 69 lock = &sync.Mutex{} 70 n.locks[name] = lock 71 } 72 return lock 73}