예제 #1
0
파일: stm.go 프로젝트: oywc410/etcd
func isKeyCurrent(k string, r *v3.GetResponse) v3.Cmp {
	rev := r.Header.Revision + 1
	if len(r.Kvs) != 0 {
		rev = r.Kvs[0].ModRevision + 1
	}
	return v3.Compare(v3.ModifiedRevision(k), "<", rev)
}
예제 #2
0
func (s *store) conditionalDelete(ctx context.Context, key string, out runtime.Object, v reflect.Value, precondtions *storage.Preconditions) error {
	getResp, err := s.client.KV.Get(ctx, key)
	if err != nil {
		return err
	}
	for {
		origState, err := s.getState(getResp, key, v, false)
		if err != nil {
			return err
		}
		if err := checkPreconditions(key, precondtions, origState.obj); err != nil {
			return err
		}
		txnResp, err := s.client.KV.Txn(ctx).If(
			clientv3.Compare(clientv3.ModifiedRevision(key), "=", origState.rev),
		).Then(
			clientv3.OpDelete(key),
		).Else(
			clientv3.OpGet(key),
		).Commit()
		if err != nil {
			return err
		}
		if !txnResp.Succeeded {
			getResp = (*clientv3.GetResponse)(txnResp.Responses[0].GetResponseRange())
			glog.V(4).Infof("deletion of %s failed because of a conflict, going to retry", key)
			continue
		}
		return decode(s.codec, s.versioner, origState.data, out, origState.rev)
	}
}
예제 #3
0
파일: client.go 프로젝트: rhuss/gofabric8
// deleteRevKey deletes a key by revision, returning false if key is missing
func deleteRevKey(kv v3.KV, key string, rev int64) (bool, error) {
	cmp := v3.Compare(v3.ModifiedRevision(key), "=", rev)
	req := v3.OpDelete(key)
	txnresp, err := kv.Txn(context.TODO()).If(cmp).Then(req).Commit()
	if err != nil {
		return false, err
	} else if txnresp.Succeeded == false {
		return false, nil
	}
	return true, nil
}
예제 #4
0
// GuaranteedUpdate implements storage.Interface.GuaranteedUpdate.
func (s *store) GuaranteedUpdate(ctx context.Context, key string, out runtime.Object, ignoreNotFound bool, precondtions *storage.Preconditions, tryUpdate storage.UpdateFunc) error {
	v, err := conversion.EnforcePtr(out)
	if err != nil {
		panic("unable to convert output object to pointer")
	}
	key = keyWithPrefix(s.pathPrefix, key)
	getResp, err := s.client.KV.Get(ctx, key)
	if err != nil {
		return err
	}
	for {
		origState, err := s.getState(getResp, key, v, ignoreNotFound)
		if err != nil {
			return err
		}

		if err := checkPreconditions(key, precondtions, origState.obj); err != nil {
			return err
		}

		ret, err := s.updateState(origState, tryUpdate)
		if err != nil {
			return err
		}

		data, err := runtime.Encode(s.codec, ret)
		if err != nil {
			return err
		}
		if bytes.Equal(data, origState.data) {
			return decode(s.codec, s.versioner, origState.data, out, origState.rev)
		}

		txnResp, err := s.client.KV.Txn(ctx).If(
			clientv3.Compare(clientv3.ModifiedRevision(key), "=", origState.rev),
		).Then(
			clientv3.OpPut(key, string(data)),
		).Else(
			clientv3.OpGet(key),
		).Commit()
		if err != nil {
			return err
		}
		if !txnResp.Succeeded {
			getResp = (*clientv3.GetResponse)(txnResp.Responses[0].GetResponseRange())
			glog.V(4).Infof("GuaranteedUpdate of %s failed because of a conflict, going to retry", key)
			continue
		}
		putResp := txnResp.Responses[0].GetResponsePut()
		return decode(s.codec, s.versioner, data, out, putResp.Header.Revision)
	}
}
예제 #5
0
파일: txn_command.go 프로젝트: lrita/etcd
func parseCompare(line string) (*clientv3.Cmp, error) {
	var (
		key string
		op  string
		val string
	)

	lparenSplit := strings.SplitN(line, "(", 2)
	if len(lparenSplit) != 2 {
		return nil, fmt.Errorf("malformed comparison: %s", line)
	}

	target := lparenSplit[0]
	n, serr := fmt.Sscanf(lparenSplit[1], "%q) %s %q", &key, &op, &val)
	if n != 3 {
		return nil, fmt.Errorf("malformed comparison: %s; got %s(%q) %s %q", line, target, key, op, val)
	}
	if serr != nil {
		return nil, fmt.Errorf("malformed comparison: %s (%v)", line, serr)
	}

	var (
		v   int64
		err error
		cmp clientv3.Cmp
	)
	switch target {
	case "ver", "version":
		if v, err = strconv.ParseInt(val, 10, 64); err == nil {
			cmp = clientv3.Compare(clientv3.Version(key), op, v)
		}
	case "c", "create":
		if v, err = strconv.ParseInt(val, 10, 64); err == nil {
			cmp = clientv3.Compare(clientv3.CreatedRevision(key), op, v)
		}
	case "m", "mod":
		if v, err = strconv.ParseInt(val, 10, 64); err == nil {
			cmp = clientv3.Compare(clientv3.ModifiedRevision(key), op, v)
		}
	case "val", "value":
		cmp = clientv3.Compare(clientv3.Value(key), op, val)
	default:
		return nil, fmt.Errorf("malformed comparison: %s (unknown target %s)", line, target)
	}

	if err != nil {
		return nil, fmt.Errorf("invalid txn compare request: %s", line)
	}

	return &cmp, nil
}
예제 #6
0
파일: key.go 프로젝트: siddontang/etcd
func NewUniqueKV(ctx context.Context, kv v3.KV, pfx, val string, opts ...v3.OpOption) (string, int64, error) {
	for {
		newKey := fmt.Sprintf("%s/%v", pfx, time.Now().UnixNano())
		put := v3.OpPut(newKey, val, opts...)
		cmp := v3.Compare(v3.ModifiedRevision(newKey), "=", 0)
		resp, err := kv.Txn(ctx).If(cmp).Then(put).Commit()
		if err != nil {
			return "", 0, err
		}
		if !resp.Succeeded {
			continue
		}
		return newKey, resp.Header.Revision, nil
	}
}
예제 #7
0
파일: stm.go 프로젝트: youtube/doorman
// commit attempts to apply the txn's changes to the server.
func (s *STM) commit() (ok bool, rr error) {
	// read set must not change
	cmps := make([]v3.Cmp, 0, len(s.rset))
	for k, rk := range s.rset {
		// use < to support updating keys that don't exist yet
		cmp := v3.Compare(v3.ModifiedRevision(k), "<", rk.Revision()+1)
		cmps = append(cmps, cmp)
	}

	// apply all writes
	puts := make([]v3.Op, 0, len(s.wset))
	for k, v := range s.wset {
		puts = append(puts, v3.OpPut(k, v))
	}
	txnresp, err := s.client.Txn(context.TODO()).If(cmps...).Then(puts...).Commit()
	return txnresp.Succeeded, err
}
예제 #8
0
파일: key.go 프로젝트: lrita/etcd
// newSequentialKV allocates a new sequential key <prefix>/nnnnn with a given
// value and lease.  Note: a bookkeeping node __<prefix> is also allocated.
func newSequentialKV(kv v3.KV, prefix, val string, leaseID lease.LeaseID) (*RemoteKV, error) {
	resp, err := kv.Get(context.TODO(), prefix, v3.WithLastKey()...)
	if err != nil {
		return nil, err
	}

	// add 1 to last key, if any
	newSeqNum := 0
	if len(resp.Kvs) != 0 {
		fields := strings.Split(string(resp.Kvs[0].Key), "/")
		_, serr := fmt.Sscanf(fields[len(fields)-1], "%d", &newSeqNum)
		if serr != nil {
			return nil, serr
		}
		newSeqNum++
	}
	newKey := fmt.Sprintf("%s/%016d", prefix, newSeqNum)

	// base prefix key must be current (i.e., <=) with the server update;
	// the base key is important to avoid the following:
	// N1: LastKey() == 1, start txn.
	// N2: New Key 2, New Key 3, Delete Key 2
	// N1: txn succeeds allocating key 2 when it shouldn't
	baseKey := "__" + prefix

	// current revision might contain modification so +1
	cmp := v3.Compare(v3.ModifiedRevision(baseKey), "<", resp.Header.Revision+1)
	reqPrefix := v3.OpPut(baseKey, "", v3.WithLease(leaseID))
	reqNewKey := v3.OpPut(newKey, val, v3.WithLease(leaseID))

	txn := kv.Txn(context.TODO())
	txnresp, err := txn.If(cmp).Then(reqPrefix, reqNewKey).Commit()
	if err != nil {
		return nil, err
	}
	if txnresp.Succeeded == false {
		return newSequentialKV(kv, prefix, val, leaseID)
	}
	return &RemoteKV{kv, newKey, txnresp.Header.Revision, val}, nil
}
예제 #9
0
func notFound(key string) clientv3.Cmp {
	return clientv3.Compare(clientv3.ModifiedRevision(key), "=", 0)
}