package service import ( "context" "database/sql" "fmt" "hash/fnv" "time" ) func hashAdvisoryLockID(key string) int64 { h := fnv.New64a() _, _ = h.Write([]byte(key)) return int64(h.Sum64()) } func tryAcquireDBAdvisoryLock(ctx context.Context, db *sql.DB, lockID int64) (func(), bool) { release, acquired, _ := tryAcquireDBAdvisoryLockWithError(ctx, db, lockID) return release, acquired } func tryAcquireDBAdvisoryLockWithError(ctx context.Context, db *sql.DB, lockID int64) (func(), bool, error) { if db == nil { return nil, false, nil } if ctx == nil { ctx = context.Background() } conn, err := db.Conn(ctx) if err != nil { return nil, false, fmt.Errorf("open advisory-lock connection: %w", err) } acquired := false if err := conn.QueryRowContext(ctx, "SELECT pg_try_advisory_lock($1)", lockID).Scan(&acquired); err != nil { _ = conn.Close() return nil, false, fmt.Errorf("query advisory lock: %w", err) } if !acquired { _ = conn.Close() return nil, false, nil } release := func() { unlockCtx, cancel := context.WithTimeout(context.Background(), 2*time.Second) defer cancel() _, _ = conn.ExecContext(unlockCtx, "SELECT pg_advisory_unlock($1)", lockID) _ = conn.Close() } return release, true, nil }