Files
go-sip/internal/dispatcher/lease.go
T

73 lines
1.5 KiB
Go

package dispatcher
import (
"context"
"fmt"
"sync"
"time"
"git.ipao.vip/rogee/go-sip/internal/store"
)
const activeLeaseScope = "dispatcher"
type LeaseGuard struct {
store *store.Store
leaseID string
scope string
holder string
ttl time.Duration
stop chan struct{}
done chan struct{}
lost chan error
once sync.Once
}
func StartLease(ctx context.Context, s *store.Store, leaseID, scope, holder string, ttl time.Duration) (*LeaseGuard, error) {
if ctx == nil {
return nil, fmt.Errorf("lease context is required")
}
lease, err := s.AcquireLease(leaseID, scope, holder, ttl)
if err != nil {
return nil, err
}
guard := &LeaseGuard{store: s, leaseID: lease.LeaseID, scope: scope, holder: holder, ttl: ttl, stop: make(chan struct{}), done: make(chan struct{}), lost: make(chan error, 1)}
go guard.renew(ctx)
return guard, nil
}
func (g *LeaseGuard) renew(ctx context.Context) {
defer close(g.done)
ticker := time.NewTicker(g.ttl / 3)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
return
case <-g.stop:
return
case <-ticker.C:
if _, err := g.store.AcquireLease(g.leaseID, g.scope, g.holder, g.ttl); err != nil {
select {
case g.lost <- err:
default:
}
return
}
}
}
}
func (g *LeaseGuard) Lost() <-chan error { return g.lost }
func (g *LeaseGuard) Stop() error {
if g == nil {
return nil
}
g.once.Do(func() {
close(g.stop)
<-g.done
})
return g.store.ReleaseLease(g.leaseID)
}