Skip to content

Commit

Permalink
v3api: Add a flag to RangeRequest to return only the keys.
Browse files Browse the repository at this point in the history
Currently the user can't list only the keys in a prefix search. In
order to support such operations the filtering will be done on the
server side to reduce the encoding and network transfer costs.
  • Loading branch information
ajityagaty committed Jun 19, 2016
1 parent b03c832 commit ad5d55d
Show file tree
Hide file tree
Showing 8 changed files with 284 additions and 197 deletions.
5 changes: 5 additions & 0 deletions Documentation/dev-guide/apispec/swagger/rpc.swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -935,6 +935,11 @@
"format": "byte",
"description": "key is the first key for the range. If range_end is not given, the request only looks up key."
},
"keys_only": {
"type": "boolean",
"format": "boolean",
"description": "keys_only when set returns only the keys and not the values."
},
"limit": {
"type": "string",
"format": "int64",
Expand Down
9 changes: 8 additions & 1 deletion clientv3/kv.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,14 @@ func (kv *kv) do(ctx context.Context, op Op) (OpResponse, error) {
// TODO: handle other ops
case tRange:
var resp *pb.RangeResponse
r := &pb.RangeRequest{Key: op.key, RangeEnd: op.end, Limit: op.limit, Revision: op.rev, Serializable: op.serializable}
r := &pb.RangeRequest{
Key: op.key,
RangeEnd: op.end,
Limit: op.limit,
Revision: op.rev,
Serializable: op.serializable,
KeysOnly: op.keysOnly,
}
if op.sort != nil {
r.SortOrder = pb.RangeRequest_SortOrder(op.sort.Order)
r.SortTarget = pb.RangeRequest_SortTarget(op.sort.Target)
Expand Down
7 changes: 7 additions & 0 deletions clientv3/op.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ type Op struct {
limit int64
sort *SortOption
serializable bool
keysOnly bool

// for range, watch
rev int64
Expand Down Expand Up @@ -208,6 +209,12 @@ func WithSerializable() OpOption {
return func(op *Op) { op.serializable = true }
}

// WithKeysOnly makes the 'Get' request return only the keys and the corresponding
// values will be omitted.
func WithKeysOnly() OpOption {
return func(op *Op) { op.keysOnly = true }
}

// WithFirstCreate gets the key with the oldest creation revision in the request range.
func WithFirstCreate() []OpOption { return withTop(SortByCreateRevision, SortAscend) }

Expand Down
24 changes: 22 additions & 2 deletions e2e/ctl_v3_kv_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,9 @@ func TestCtlV3GetPeerTLS(t *testing.T) { testCtl(t, getTest, withCfg(confi
func TestCtlV3GetTimeout(t *testing.T) { testCtl(t, getTest, withDialTimeout(0)) }
func TestCtlV3GetQuorum(t *testing.T) { testCtl(t, getTest, withQuorum()) }

func TestCtlV3GetFormat(t *testing.T) { testCtl(t, getFormatTest) }
func TestCtlV3GetRev(t *testing.T) { testCtl(t, getRevTest) }
func TestCtlV3GetFormat(t *testing.T) { testCtl(t, getFormatTest) }
func TestCtlV3GetRev(t *testing.T) { testCtl(t, getRevTest) }
func TestCtlV3GetKeysOnly(t *testing.T) { testCtl(t, getKeysOnlyTest) }

func TestCtlV3Del(t *testing.T) { testCtl(t, delTest) }
func TestCtlV3DelNoTLS(t *testing.T) { testCtl(t, delTest, withCfg(configNoTLS)) }
Expand Down Expand Up @@ -145,6 +146,25 @@ func getRevTest(cx ctlCtx) {
}
}

func getKeysOnlyTest(cx ctlCtx) {
var (
kvs = []kv{{"key1", "val1"}}
)
for i := range kvs {
if err := ctlV3Put(cx, kvs[i].key, kvs[i].val, ""); err != nil {
cx.t.Fatalf("getKeysOnlyTest #%d: ctlV3Put error (%v)", i, err)
}
}

cmdArgs := append(cx.PrefixArgs(), "get")
cmdArgs = append(cmdArgs, []string{"--prefix", "--keys-only", "key"}...)

err := spawnWithExpects(cmdArgs, []string{"key1", ""}...)
if err != nil {
cx.t.Fatalf("getKeysOnlyTest : error (%v)", err)
}
}

func delTest(cx ctlCtx) {
tests := []struct {
puts []kv
Expand Down
6 changes: 6 additions & 0 deletions etcdctl/ctlv3/command/get_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ var (
getPrefix bool
getFromKey bool
getRev int64
getKeysOnly bool
)

// NewGetCommand returns the cobra command for "get".
Expand All @@ -47,6 +48,7 @@ func NewGetCommand() *cobra.Command {
cmd.Flags().BoolVar(&getPrefix, "prefix", false, "get keys with matching prefix")
cmd.Flags().BoolVar(&getFromKey, "from-key", false, "get keys that are greater than or equal to the given key")
cmd.Flags().Int64Var(&getRev, "rev", 0, "specify the kv revision")
cmd.Flags().BoolVar(&getKeysOnly, "keys-only", false, "get only the keys")
return cmd
}

Expand Down Expand Up @@ -144,5 +146,9 @@ func getGetOp(cmd *cobra.Command, args []string) (string, []clientv3.OpOption) {
opts = append(opts, clientv3.WithFromKey())
}

if getKeysOnly {
opts = append(opts, clientv3.WithKeysOnly())
}

return key, opts
}
3 changes: 3 additions & 0 deletions etcdserver/apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,9 @@ func (a *applierV3backend) Range(txnID int64, r *pb.RangeRequest) (*pb.RangeResp

resp.Header.Revision = rev
for i := range kvs {
if r.KeysOnly {
kvs[i].Value = nil
}
resp.Kvs = append(resp.Kvs, &kvs[i])
}
return resp, nil
Expand Down
424 changes: 230 additions & 194 deletions etcdserver/etcdserverpb/rpc.pb.go

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions etcdserver/etcdserverpb/rpc.proto
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,9 @@ message RangeRequest {
// a serializable range request is served locally without needing to reach consensus
// with other nodes in the cluster.
bool serializable = 7;

// keys_only when set returns only the keys and not the values.
bool keys_only = 8;
}

message RangeResponse {
Expand Down

0 comments on commit ad5d55d

Please sign in to comment.