func (kv *memKV) Put( key string, value interface{}, ttl uint64, ) (*kvdb.KVPair, error) { var kvp *kvdb.KVPair suffix := key key = kv.domain + suffix kv.mutex.Lock() defer kv.mutex.Unlock() index := atomic.AddUint64(&kv.index, 1) if ttl != 0 { time.AfterFunc(time.Second*time.Duration(ttl), func() { // TODO: handle error _, _ = kv.Delete(suffix) }) } b, err := common.ToBytes(value) if err != nil { return nil, err } if old, ok := kv.m[key]; ok { old.Value = b old.Action = kvdb.KVSet old.ModifiedIndex = index old.KVDBIndex = index kvp = old } else { kvp = &kvdb.KVPair{ Key: key, Value: b, TTL: int64(ttl), KVDBIndex: index, ModifiedIndex: index, CreatedIndex: index, Action: kvdb.KVCreate, } kv.m[key] = kvp } kv.normalize(kvp) go kv.fireCB(key, *kvp, nil) return kvp, nil }
func (kv *etcdKV) Update( key string, val interface{}, ttl uint64, ) (*kvdb.KVPair, error) { key = kv.domain + key b, err := common.ToBytes(val) if err != nil { return nil, err } return kv.setWithRetry(context.Background(), key, string(b), &e.SetOptions{ TTL: time.Duration(ttl) * time.Second, PrevExist: e.PrevExist, }) }
func (kv *consulKV) Put(key string, val interface{}, ttl uint64) (*kvdb.KVPair, error) { pathKey := kv.domain + key pathKey = stripConsecutiveForwardslash(pathKey) b, err := common.ToBytes(val) if err != nil { return nil, err } pair := &api.KVPair{ Key: pathKey, Value: b, } if ttl > 0 { if ttl < 20 { return nil, kvdb.ErrTTLNotSupported } // Future Use : To Support TTL values for retries := 1; retries <= MaxRenewRetries; retries++ { // Consul doubles the ttl value. Hence we divide it by 2 // Consul does not support ttl values less than 10. // Hence we set our lower limit to 20 err := kv.renewSession(pair, ttl/2) if err == nil { break } if retries == MaxRenewRetries { return nil, kvdb.ErrSetTTLFailed } } } if _, err := kv.client.KV().Put(pair, nil); err != nil { return nil, err } kvPair, err := kv.Get(key) if err != nil { return nil, err } kvPair.Action = kvdb.KVSet return kvPair, nil }
func (kv *consulKV) getLock(key string, tag interface{}, ttl time.Duration) ( *consulLock, error, ) { key = kv.domain + key tagValue, err := common.ToBytes(tag) if err != nil { return nil, fmt.Errorf("Failed to convert tag: %v, error: %v", tag, err) } lockOpts := &api.LockOptions{ Key: key, Value: tagValue, } lock := &consulLock{} entry := &api.SessionEntry{ Behavior: api.SessionBehaviorRelease, // Release the lock when the session expires TTL: (ttl / 2).String(), // Consul multiplies the TTL by 2x LockDelay: 1 * time.Millisecond, // Virtually disable lock delay } // Create the key session session, _, err := kv.client.Session().Create(entry, nil) if err != nil { return nil, err } // Place the session on lock lockOpts.Session = session lock.doneCh = make(chan struct{}) l, err := kv.client.LockOpts(lockOpts) if err != nil { return nil, err } kv.renewLockSession(entry.TTL, session, lock.doneCh) lock.lock = l return lock, nil }
func (kv *consulKV) Snapshot(prefix string) (kvdb.Kvdb, uint64, error) { // Create a new bootstrap key : lowest index r := rand.New(rand.NewSource(time.Now().UnixNano())).Int63() bootStrapKeyLow := bootstrap + strconv.FormatInt(r, 10) + strconv.FormatInt(time.Now().UnixNano(), 10) val, _ := common.ToBytes(time.Now().UnixNano()) kvPair, err := kv.Put(bootStrapKeyLow, val, 0) if err != nil { return nil, 0, fmt.Errorf("Failed to create snap bootstrap key %v, "+ "err: %v", bootStrapKeyLow, err) } lowestKvdbIndex := kvPair.ModifiedIndex options := &api.QueryOptions{ AllowStale: false, RequireConsistent: true, } listKey := kv.domain + prefix listKey = stripConsecutiveForwardslash(listKey) pairs, _, err := kv.client.KV().List(listKey, options) if err != nil { return nil, 0, err } kvPairs := kv.pairToKvs("enumerate", pairs, nil) snapDb, err := mem.New( kv.domain, nil, map[string]string{mem.KvSnap: "true"}, kv.FatalCb, ) if err != nil { return nil, 0, err } for _, kvPair := range kvPairs { _, err := snapDb.SnapPut(kvPair) if err != nil { return nil, 0, fmt.Errorf("Failed creating snap: %v", err) } } // Create bootrap key : highest index bootStrapKeyHigh := bootstrap + strconv.FormatInt(r, 10) + strconv.FormatInt(time.Now().UnixNano(), 10) val, _ = common.ToBytes(time.Now().UnixNano()) kvPair, err = kv.Put(bootStrapKeyHigh, val, 0) if err != nil { return nil, 0, fmt.Errorf("Failed to create snap bootstrap key %v, "+ "err: %v", bootStrapKeyHigh, err) } highestKvdbIndex := kvPair.ModifiedIndex // In consul Delete does not increment kvdb index. // Hence the put (bootstrap) key and delete both return the same index if lowestKvdbIndex+1 != highestKvdbIndex { // create a watch to get all changes // between lowestKvdbIndex and highestKvdbIndex done := make(chan error) mutex := &sync.Mutex{} cb := func( prefix string, opaque interface{}, kvp *kvdb.KVPair, err error, ) error { var watchErr error var sendErr error var m *sync.Mutex ok := false if err != nil { if err == kvdb.ErrWatchStopped { return nil } watchErr = err sendErr = err goto errordone } if kvp == nil { watchErr = fmt.Errorf("kvp is nil") sendErr = watchErr goto errordone } m, ok = opaque.(*sync.Mutex) if !ok { watchErr = fmt.Errorf("Failed to get mutex") sendErr = watchErr goto errordone } m.Lock() defer m.Unlock() if kvp.ModifiedIndex > highestKvdbIndex { // done applying changes, just return watchErr = fmt.Errorf("done") sendErr = nil goto errordone } else if kvp.ModifiedIndex == highestKvdbIndex { // last update that we needed. Put it inside snap db // and return _, err = snapDb.SnapPut(kvp) if err != nil { watchErr = fmt.Errorf("Failed to apply update to snap: %v", err) sendErr = watchErr } else { watchErr = fmt.Errorf("done") sendErr = nil } goto errordone } else { _, err = snapDb.SnapPut(kvp) if err != nil { watchErr = fmt.Errorf("Failed to apply update to snap: %v", err) sendErr = watchErr goto errordone } } return nil errordone: done <- sendErr return watchErr } if err := kv.WatchTree("", lowestKvdbIndex, mutex, cb); err != nil { return nil, 0, fmt.Errorf("Failed to start watch: %v", err) } err = <-done if err != nil { return nil, 0, err } } _, err = kv.Delete(bootStrapKeyLow) if err != nil { return nil, 0, fmt.Errorf("Failed to delete snap bootstrap key: %v, "+ "err: %v", bootStrapKeyLow, err) } _, err = kv.Delete(bootStrapKeyHigh) if err != nil { return nil, 0, fmt.Errorf("Failed to delete snap bootstrap key: %v, "+ "err: %v", bootStrapKeyHigh, err) } return snapDb, highestKvdbIndex, nil }