// setupLock is used to setup a new Lock given the API client, the key prefix to // operate on, and an optional session name. If oneshot is true then we will set // up for a single attempt at acquisition, using the given wait time. The retry // parameter sets how many 500 errors the lock monitor will tolerate before // giving up the lock. func (c *LockCommand) setupLock(client *api.Client, prefix, name string, oneshot bool, wait time.Duration, retry int) (*LockUnlock, error) { // Use the DefaultSemaphoreKey extension, this way if a lock and // semaphore are both used at the same prefix, we will get a conflict // which we can report to the user. key := path.Join(prefix, api.DefaultSemaphoreKey) if c.verbose { c.Ui.Info(fmt.Sprintf("Setting up lock at path: %s", key)) } opts := api.LockOptions{ Key: key, SessionName: name, MonitorRetries: retry, MonitorRetryTime: defaultMonitorRetryTime, } if oneshot { opts.LockTryOnce = true opts.LockWaitTime = wait } l, err := client.LockOpts(&opts) if err != nil { return nil, err } lu := &LockUnlock{ lockFn: l.Lock, unlockFn: l.Unlock, cleanupFn: l.Destroy, inUseErr: api.ErrLockInUse, rawOpts: &opts, } return lu, nil }
func (d *DedupManager) attemptLock(client *consulapi.Client, session string, sessionCh chan struct{}, t *Template) { defer d.wg.Done() START: log.Printf("[INFO] (dedup) attempting lock for template hash %s", t.hexMD5) basePath := path.Join(d.config.Deduplicate.Prefix, t.hexMD5) lopts := &consulapi.LockOptions{ Key: path.Join(basePath, "lock"), Session: session, MonitorRetries: 3, MonitorRetryTime: 3 * time.Second, } lock, err := client.LockOpts(lopts) if err != nil { log.Printf("[ERR] (dedup) failed to create lock '%s': %v", lopts.Key, err) return } var retryCh <-chan time.Time leaderCh, err := lock.Lock(sessionCh) if err != nil { log.Printf("[ERR] (dedup) failed to acquire lock '%s': %v", lopts.Key, err) retryCh = time.After(lockRetry) } else { log.Printf("[INFO] (dedup) acquired lock '%s'", lopts.Key) d.setLeader(t, leaderCh) } select { case <-retryCh: retryCh = nil goto START case <-leaderCh: log.Printf("[WARN] (dedup) lost lock ownership '%s'", lopts.Key) d.setLeader(t, nil) goto START case <-sessionCh: log.Printf("[INFO] (dedup) releasing lock '%s'", lopts.Key) d.setLeader(t, nil) lock.Unlock() case <-d.stopCh: log.Printf("[INFO] (dedup) releasing lock '%s'", lopts.Key) lock.Unlock() } }
// setupLock is used to setup a new Lock given the API client, // the key prefix to operate on, and an optional session name. func (c *LockCommand) setupLock(client *api.Client, prefix, name string) (*LockUnlock, error) { // Use the DefaultSemaphoreKey extension, this way if a lock and // semaphore are both used at the same prefix, we will get a conflict // which we can report to the user. key := path.Join(prefix, api.DefaultSemaphoreKey) if c.verbose { c.Ui.Info(fmt.Sprintf("Setting up lock at path: %s", key)) } opts := api.LockOptions{ Key: key, SessionName: name, } l, err := client.LockOpts(&opts) if err != nil { return nil, err } lu := &LockUnlock{ lockFn: l.Lock, unlockFn: l.Unlock, cleanupFn: l.Destroy, inUseErr: api.ErrLockInUse, } return lu, nil }