Esempio n. 1
0
// 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
}
Esempio n. 2
0
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
}
Esempio n. 3
0
// 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
}
Esempio n. 4
0
// 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
}