// Free a lock. Pass force=true to force it dead. func (c *Client) Free(lock db.Lock, force bool) error { content, err := jsonio.Write(lock) if err != nil { return errors.LockFailed.Combine(err) } path, err := lock.Path() if err != nil { return errors.LockFailed.Combine(err) } opts := &client.DeleteOptions{PrevValue: string(content)} if force { opts = &client.DeleteOptions{} } _, err = c.client.Delete(context.Background(), c.qualified(path), opts) return err }
func (c *Client) doAcquire(lock db.Lock, ttl time.Duration) error { content, err := jsonio.Write(lock) if err != nil { return errors.LockFailed.Combine(err) } path, err := lock.Path() if err != nil { return errors.LockFailed.Combine(err) } _, err = c.client.Set(context.Background(), c.qualified(path), string(content), &client.SetOptions{PrevValue: string(content), TTL: ttl}) if er, ok := err.(client.Error); ok && er.Code == client.ErrorCodeKeyNotFound { _, err := c.client.Set(context.Background(), c.qualified(path), string(content), &client.SetOptions{PrevExist: client.PrevNoExist, TTL: ttl}) if err != nil { return errors.LockFailed.Combine(err) } return nil } return err }
// Free a lock. Passing true as the second parameter will force the removal. func (c *Client) Free(obj db.Lock, force bool) error { path, err := obj.Path() if err != nil { return errors.LockFailed.Combine(err) } logrus.Debugf("Attempting to free %q by %v", path, obj) mylock, ok := c.getLock(path) if !ok { return errors.LockFailed.Combine(errored.New("Could not locate lock")) } if !reflect.DeepEqual(obj, mylock.obj) { if force { goto free } return errors.LockFailed.Combine(errored.New("invalid lock requested to be freed (wrong host?)")) } free: select { case <-mylock.monitorChan: default: mylock.lock.Unlock() } c.lockMutex.Lock() if _, ok := c.locks[path]; ok { delete(c.locks, path) } c.Delete(mylock.obj) c.lockMutex.Unlock() return nil }
// acquires and yields a *lock{} populated with all the values needed to // persist and reap this lock. Returns true if it is already running and we // have re-requested a running lock. func (c *Client) lock(obj db.Lock, ttl time.Duration, refresh bool) (*lock, bool, error) { // consul has a minimum ttl of 10s. Adjust. if ttl < 10*time.Second { ttl = 10 * time.Second } path, err := obj.Path() if err != nil { return nil, false, errors.LockFailed.Combine(err) } tmplock, ok := c.getLock(path) if ok { if reflect.DeepEqual(tmplock.obj, obj) { return tmplock, true, nil } return nil, false, errors.LockFailed.Combine(errored.Errorf("Invalid lock attempted at %q -- already exists", path)) } sessionID, err := c.getSession(obj, ttl, refresh) if err != nil { return nil, false, err } content, err := jsonio.Write(obj) if err != nil { return nil, false, err } mylock, err := c.client.LockOpts(&api.LockOptions{ LockTryOnce: true, Key: c.qualified(path), Value: content, SessionTTL: ttl.String(), MonitorRetryTime: ttl, MonitorRetries: -1, }) if err != nil { return nil, false, err } stopChan := make(chan struct{}) go func() { time.Sleep(100 * time.Millisecond) close(stopChan) }() monitor, err := mylock.Lock(stopChan) if err != nil { return nil, false, err } done := make(chan struct{}) lockObj := &lock{ lock: mylock, sessionID: sessionID, doneChan: done, monitorChan: monitor, obj: obj, ttl: ttl, path: path, refresh: refresh, } return lockObj, false, nil }