discrawl/internal/cli/sync_lock.go
2026-05-08 09:49:13 +01:00

101 lines
2.1 KiB
Go

package cli
import (
"context"
"fmt"
"os"
"path/filepath"
"strings"
"time"
"github.com/openclaw/discrawl/internal/config"
)
func (r *runtime) withSyncLock(fn func() error) error {
if r.dbLockHeld {
return fn()
}
lockPath, err := r.syncLockPath()
if err != nil {
return err
}
release, err := acquireSyncLock(r.ctx, lockPath)
if err != nil {
return err
}
r.dbLockHeld = true
r.lockStarted = r.nowUTC()
r.setSyncLockPhase("locked")
defer func() {
r.dbLockHeld = false
r.lockStarted = time.Time{}
_ = release()
}()
return fn()
}
func (r *runtime) tryWithSyncLock(fn func() error) (bool, error) {
if r.dbLockHeld {
return true, fn()
}
lockPath, err := r.syncLockPath()
if err != nil {
return false, err
}
release, locked, err := tryAcquireSyncLock(lockPath)
if err != nil || !locked {
return locked, err
}
r.dbLockHeld = true
r.lockStarted = r.nowUTC()
r.setSyncLockPhase("locked")
defer func() {
r.dbLockHeld = false
r.lockStarted = time.Time{}
_ = release()
}()
return true, fn()
}
func (r *runtime) setSyncLockPhase(phase string) {
if !r.dbLockHeld {
return
}
path, err := r.syncLockPath()
if err != nil {
return
}
started := r.lockStarted
if started.IsZero() {
started = r.nowUTC()
}
body := fmt.Sprintf("pid=%d\nstarted_at=%s\nupdated_at=%s\nphase=%s\n",
os.Getpid(),
started.Format(time.RFC3339Nano),
r.nowUTC().Format(time.RFC3339Nano),
phase,
)
_ = os.WriteFile(path, []byte(body), 0o600)
}
func (r *runtime) syncLockPath() (string, error) {
dbPath, err := config.ExpandPath(r.cfg.DBPath)
if err != nil {
return "", configErr(err)
}
return filepath.Join(filepath.Dir(dbPath), ".discrawl-sync.lock"), nil
}
func syncLockErr(ctx context.Context, path string) error {
if ctx.Err() != nil {
if body, err := os.ReadFile(path); err == nil {
details := strings.TrimSpace(string(body))
if details != "" {
return fmt.Errorf("wait for sync lock %s (%s): %w", path, strings.ReplaceAll(details, "\n", ", "), ctx.Err())
}
}
return fmt.Errorf("wait for sync lock %s: %w", path, ctx.Err())
}
return nil
}