mirror of
https://github.com/etcd-io/etcd.git
synced 2024-09-27 06:25:44 +00:00
tests/robustness: Implement proper range requests
Signed-off-by: Marek Siarkowicz <siarkowicz@google.com>
This commit is contained in:
parent
f7831e2607
commit
2e7cb772b2
@ -126,14 +126,18 @@ func describeRangeRequest(opts RangeOptions, revision int64) string {
|
||||
if opts.Limit != 0 {
|
||||
kwargs = append(kwargs, fmt.Sprintf("limit=%d", opts.Limit))
|
||||
}
|
||||
command := "get"
|
||||
if opts.WithPrefix {
|
||||
command = "range"
|
||||
kwargsString := strings.Join(kwargs, ", ")
|
||||
if kwargsString != "" {
|
||||
kwargsString = ", " + kwargsString
|
||||
}
|
||||
if len(kwargs) == 0 {
|
||||
return fmt.Sprintf("%s(%q)", command, opts.Key)
|
||||
switch {
|
||||
case opts.End == "":
|
||||
return fmt.Sprintf("get(%q%s)", opts.Start, kwargsString)
|
||||
case opts.End == prefixEnd(opts.Start):
|
||||
return fmt.Sprintf("list(%q%s)", opts.Start, kwargsString)
|
||||
default:
|
||||
return fmt.Sprintf("range(%q..%q%s)", opts.Start, opts.End, kwargsString)
|
||||
}
|
||||
return fmt.Sprintf("%s(%q, %s)", command, opts.Key, strings.Join(kwargs, ", "))
|
||||
}
|
||||
|
||||
func describeEtcdOperationResponse(op EtcdOperation, resp EtcdOperationResult) string {
|
||||
@ -149,8 +153,8 @@ func describeEtcdOperationResponse(op EtcdOperation, resp EtcdOperationResult) s
|
||||
}
|
||||
}
|
||||
|
||||
func describeRangeResponse(opts RangeOptions, response RangeResponse) string {
|
||||
if opts.WithPrefix {
|
||||
func describeRangeResponse(request RangeOptions, response RangeResponse) string {
|
||||
if request.End != "" {
|
||||
kvs := make([]string, len(response.KVs))
|
||||
for i, kv := range response.KVs {
|
||||
kvs[i] = describeValueOrHash(kv.Value)
|
||||
|
@ -95,17 +95,17 @@ func TestModelDescribe(t *testing.T) {
|
||||
expectDescribe: `if(mod_rev(key9)==9).then(put("key9", "99")) -> err: "failed"`,
|
||||
},
|
||||
{
|
||||
req: txnRequest([]EtcdCondition{{Key: "key9b", ExpectedRevision: 9}}, []EtcdOperation{{Type: PutOperation, Put: PutOptions{Key: "key9b", Value: ValueOrHash{Value: "991"}}}}, []EtcdOperation{{Type: RangeOperation, Range: RangeOptions{Key: "key9b"}}}),
|
||||
req: txnRequest([]EtcdCondition{{Key: "key9b", ExpectedRevision: 9}}, []EtcdOperation{{Type: PutOperation, Put: PutOptions{Key: "key9b", Value: ValueOrHash{Value: "991"}}}}, []EtcdOperation{{Type: RangeOperation, Range: RangeOptions{Start: "key9b"}}}),
|
||||
resp: txnResponse([]EtcdOperationResult{{}}, true, 10),
|
||||
expectDescribe: `if(mod_rev(key9b)==9).then(put("key9b", "991")).else(get("key9b")) -> success(ok), rev: 10`,
|
||||
},
|
||||
{
|
||||
req: txnRequest([]EtcdCondition{{Key: "key9c", ExpectedRevision: 9}}, []EtcdOperation{{Type: PutOperation, Put: PutOptions{Key: "key9c", Value: ValueOrHash{Value: "992"}}}}, []EtcdOperation{{Type: RangeOperation, Range: RangeOptions{Key: "key9c"}}}),
|
||||
req: txnRequest([]EtcdCondition{{Key: "key9c", ExpectedRevision: 9}}, []EtcdOperation{{Type: PutOperation, Put: PutOptions{Key: "key9c", Value: ValueOrHash{Value: "992"}}}}, []EtcdOperation{{Type: RangeOperation, Range: RangeOptions{Start: "key9c"}}}),
|
||||
resp: txnResponse([]EtcdOperationResult{{RangeResponse: RangeResponse{KVs: []KeyValue{{Key: "key9c", ValueRevision: ValueRevision{Value: ValueOrHash{Value: "993"}, ModRevision: 10}}}}}}, false, 10),
|
||||
expectDescribe: `if(mod_rev(key9c)==9).then(put("key9c", "992")).else(get("key9c")) -> failure("993"), rev: 10`,
|
||||
},
|
||||
{
|
||||
req: txnRequest(nil, []EtcdOperation{{Type: RangeOperation, Range: RangeOptions{Key: "10"}}, {Type: PutOperation, Put: PutOptions{Key: "11", Value: ValueOrHash{Value: "111"}}}, {Type: DeleteOperation, Delete: DeleteOptions{Key: "12"}}}, nil),
|
||||
req: txnRequest(nil, []EtcdOperation{{Type: RangeOperation, Range: RangeOptions{Start: "10"}}, {Type: PutOperation, Put: PutOptions{Key: "11", Value: ValueOrHash{Value: "111"}}}, {Type: DeleteOperation, Delete: DeleteOptions{Key: "12"}}}, nil),
|
||||
resp: txnResponse([]EtcdOperationResult{{RangeResponse: RangeResponse{KVs: []KeyValue{{ValueRevision: ValueRevision{Value: ValueOrHash{Value: "110"}}}}}}, {}, {Deleted: 1}}, true, 10),
|
||||
expectDescribe: `get("10"), put("11", "111"), delete("12") -> "110", ok, deleted: 1, rev: 10`,
|
||||
},
|
||||
@ -115,29 +115,44 @@ func TestModelDescribe(t *testing.T) {
|
||||
expectDescribe: `defragment() -> ok, rev: 10`,
|
||||
},
|
||||
{
|
||||
req: rangeRequest("key11", true, 0),
|
||||
req: listRequest("key11", 0),
|
||||
resp: rangeResponse(nil, 0, 11),
|
||||
expectDescribe: `range("key11") -> [], count: 0, rev: 11`,
|
||||
expectDescribe: `list("key11") -> [], count: 0, rev: 11`,
|
||||
},
|
||||
{
|
||||
req: rangeRequest("key12", true, 0),
|
||||
req: listRequest("key12", 0),
|
||||
resp: rangeResponse([]*mvccpb.KeyValue{{Value: []byte("12")}}, 2, 12),
|
||||
expectDescribe: `range("key12") -> ["12"], count: 2, rev: 12`,
|
||||
expectDescribe: `list("key12") -> ["12"], count: 2, rev: 12`,
|
||||
},
|
||||
{
|
||||
req: rangeRequest("key13", true, 0),
|
||||
req: listRequest("key13", 0),
|
||||
resp: rangeResponse([]*mvccpb.KeyValue{{Value: []byte("01234567890123456789")}}, 1, 13),
|
||||
expectDescribe: `range("key13") -> [hash: 2945867837], count: 1, rev: 13`,
|
||||
expectDescribe: `list("key13") -> [hash: 2945867837], count: 1, rev: 13`,
|
||||
},
|
||||
{
|
||||
req: rangeRequest("key14", true, 14),
|
||||
req: listRequest("key14", 14),
|
||||
resp: rangeResponse(nil, 0, 14),
|
||||
expectDescribe: `range("key14", limit=14) -> [], count: 0, rev: 14`,
|
||||
expectDescribe: `list("key14", limit=14) -> [], count: 0, rev: 14`,
|
||||
},
|
||||
{
|
||||
req: staleRangeRequest("key15", true, 0, 15),
|
||||
req: staleListRequest("key15", 0, 15),
|
||||
resp: rangeResponse(nil, 0, 15),
|
||||
expectDescribe: `range("key15", rev=15) -> [], count: 0, rev: 15`,
|
||||
expectDescribe: `list("key15", rev=15) -> [], count: 0, rev: 15`,
|
||||
},
|
||||
{
|
||||
req: staleListRequest("key15", 2, 15),
|
||||
resp: rangeResponse(nil, 0, 15),
|
||||
expectDescribe: `list("key15", rev=15, limit=2) -> [], count: 0, rev: 15`,
|
||||
},
|
||||
{
|
||||
req: rangeRequest("key16", "key16b", 0),
|
||||
resp: rangeResponse(nil, 0, 16),
|
||||
expectDescribe: `range("key16".."key16b") -> [], count: 0, rev: 16`,
|
||||
},
|
||||
{
|
||||
req: rangeRequest("key16", "key16b", 2),
|
||||
resp: rangeResponse(nil, 0, 16),
|
||||
expectDescribe: `range("key16".."key16b", limit=2) -> [], count: 0, rev: 16`,
|
||||
},
|
||||
}
|
||||
for _, tc := range tcs {
|
||||
|
@ -21,7 +21,6 @@ import (
|
||||
"hash/fnv"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/anishathalye/porcupine"
|
||||
)
|
||||
@ -189,10 +188,10 @@ func (s EtcdState) getRange(options RangeOptions) RangeResponse {
|
||||
response := RangeResponse{
|
||||
KVs: []KeyValue{},
|
||||
}
|
||||
if options.WithPrefix {
|
||||
if options.End != "" {
|
||||
var count int64
|
||||
for k, v := range s.KeyValues {
|
||||
if strings.HasPrefix(k, options.Key) {
|
||||
if k >= options.Start && k < options.End {
|
||||
response.KVs = append(response.KVs, KeyValue{Key: k, ValueRevision: v})
|
||||
count += 1
|
||||
}
|
||||
@ -205,10 +204,10 @@ func (s EtcdState) getRange(options RangeOptions) RangeResponse {
|
||||
}
|
||||
response.Count = count
|
||||
} else {
|
||||
value, ok := s.KeyValues[options.Key]
|
||||
value, ok := s.KeyValues[options.Start]
|
||||
if ok {
|
||||
response.KVs = append(response.KVs, KeyValue{
|
||||
Key: options.Key,
|
||||
Key: options.Start,
|
||||
ValueRevision: value,
|
||||
})
|
||||
response.Count = 1
|
||||
@ -256,9 +255,9 @@ type RangeRequest struct {
|
||||
}
|
||||
|
||||
type RangeOptions struct {
|
||||
Key string
|
||||
WithPrefix bool
|
||||
Limit int64
|
||||
Start string
|
||||
End string
|
||||
Limit int64
|
||||
}
|
||||
|
||||
type PutOptions struct {
|
||||
|
@ -83,8 +83,8 @@ var commonTestScenarios = []modelTestCase{
|
||||
operations: []testOperation{
|
||||
{req: putRequest("key1", "1"), resp: putResponse(2)},
|
||||
{req: putRequest("key2", "2"), resp: putResponse(3)},
|
||||
{req: rangeRequest("key", true, 0), resp: rangeResponse([]*mvccpb.KeyValue{{Key: []byte("key1"), Value: []byte("1"), ModRevision: 2}, {Key: []byte("key2"), Value: []byte("2"), ModRevision: 3}}, 2, 3)},
|
||||
{req: rangeRequest("key", true, 0), resp: rangeResponse([]*mvccpb.KeyValue{{Key: []byte("key1"), Value: []byte("1"), ModRevision: 2}, {Key: []byte("key2"), Value: []byte("2"), ModRevision: 3}}, 2, 3)},
|
||||
{req: listRequest("key", 0), resp: rangeResponse([]*mvccpb.KeyValue{{Key: []byte("key1"), Value: []byte("1"), ModRevision: 2}, {Key: []byte("key2"), Value: []byte("2"), ModRevision: 3}}, 2, 3)},
|
||||
{req: listRequest("key", 0), resp: rangeResponse([]*mvccpb.KeyValue{{Key: []byte("key1"), Value: []byte("1"), ModRevision: 2}, {Key: []byte("key2"), Value: []byte("2"), ModRevision: 3}}, 2, 3)},
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -93,26 +93,26 @@ var commonTestScenarios = []modelTestCase{
|
||||
{req: putRequest("key1", "1"), resp: putResponse(2)},
|
||||
{req: putRequest("key2", "2"), resp: putResponse(3)},
|
||||
{req: putRequest("key3", "3"), resp: putResponse(4)},
|
||||
{req: rangeRequest("key", true, 0), resp: rangeResponse([]*mvccpb.KeyValue{
|
||||
{req: listRequest("key", 0), resp: rangeResponse([]*mvccpb.KeyValue{
|
||||
{Key: []byte("key1"), Value: []byte("1"), ModRevision: 2},
|
||||
{Key: []byte("key2"), Value: []byte("2"), ModRevision: 3},
|
||||
{Key: []byte("key3"), Value: []byte("3"), ModRevision: 4},
|
||||
}, 3, 4)},
|
||||
{req: rangeRequest("key", true, 4), resp: rangeResponse([]*mvccpb.KeyValue{
|
||||
{req: listRequest("key", 4), resp: rangeResponse([]*mvccpb.KeyValue{
|
||||
{Key: []byte("key1"), Value: []byte("1"), ModRevision: 2},
|
||||
{Key: []byte("key2"), Value: []byte("2"), ModRevision: 3},
|
||||
{Key: []byte("key3"), Value: []byte("3"), ModRevision: 4},
|
||||
}, 3, 4)},
|
||||
{req: rangeRequest("key", true, 3), resp: rangeResponse([]*mvccpb.KeyValue{
|
||||
{req: listRequest("key", 3), resp: rangeResponse([]*mvccpb.KeyValue{
|
||||
{Key: []byte("key1"), Value: []byte("1"), ModRevision: 2},
|
||||
{Key: []byte("key2"), Value: []byte("2"), ModRevision: 3},
|
||||
{Key: []byte("key3"), Value: []byte("3"), ModRevision: 4},
|
||||
}, 3, 4)},
|
||||
{req: rangeRequest("key", true, 2), resp: rangeResponse([]*mvccpb.KeyValue{
|
||||
{req: listRequest("key", 2), resp: rangeResponse([]*mvccpb.KeyValue{
|
||||
{Key: []byte("key1"), Value: []byte("1"), ModRevision: 2},
|
||||
{Key: []byte("key2"), Value: []byte("2"), ModRevision: 3},
|
||||
}, 3, 4)},
|
||||
{req: rangeRequest("key", true, 1), resp: rangeResponse([]*mvccpb.KeyValue{
|
||||
{req: listRequest("key", 1), resp: rangeResponse([]*mvccpb.KeyValue{
|
||||
{Key: []byte("key1"), Value: []byte("1"), ModRevision: 2},
|
||||
}, 3, 4)},
|
||||
},
|
||||
@ -123,17 +123,17 @@ var commonTestScenarios = []modelTestCase{
|
||||
{req: putRequest("key3", "3"), resp: putResponse(2)},
|
||||
{req: putRequest("key2", "1"), resp: putResponse(3)},
|
||||
{req: putRequest("key1", "2"), resp: putResponse(4)},
|
||||
{req: rangeRequest("key", true, 0), resp: rangeResponse([]*mvccpb.KeyValue{
|
||||
{req: listRequest("key", 0), resp: rangeResponse([]*mvccpb.KeyValue{
|
||||
{Key: []byte("key1"), Value: []byte("2"), ModRevision: 4},
|
||||
{Key: []byte("key2"), Value: []byte("1"), ModRevision: 3},
|
||||
{Key: []byte("key3"), Value: []byte("3"), ModRevision: 2},
|
||||
}, 3, 4)},
|
||||
{req: rangeRequest("key", true, 0), resp: rangeResponse([]*mvccpb.KeyValue{
|
||||
{req: listRequest("key", 0), resp: rangeResponse([]*mvccpb.KeyValue{
|
||||
{Key: []byte("key2"), Value: []byte("1"), ModRevision: 3},
|
||||
{Key: []byte("key1"), Value: []byte("2"), ModRevision: 4},
|
||||
{Key: []byte("key3"), Value: []byte("3"), ModRevision: 2},
|
||||
}, 3, 4), expectFailure: true},
|
||||
{req: rangeRequest("key", true, 0), resp: rangeResponse([]*mvccpb.KeyValue{
|
||||
{req: listRequest("key", 0), resp: rangeResponse([]*mvccpb.KeyValue{
|
||||
{Key: []byte("key3"), Value: []byte("3"), ModRevision: 2},
|
||||
{Key: []byte("key2"), Value: []byte("1"), ModRevision: 3},
|
||||
{Key: []byte("key1"), Value: []byte("2"), ModRevision: 4},
|
||||
|
@ -59,9 +59,13 @@ func (h *AppendableHistory) AppendRange(key string, withPrefix bool, revision in
|
||||
if resp != nil && resp.Header != nil {
|
||||
respRevision = resp.Header.Revision
|
||||
}
|
||||
var keyEnd string
|
||||
if withPrefix {
|
||||
keyEnd = prefixEnd(key)
|
||||
}
|
||||
h.appendSuccessful(porcupine.Operation{
|
||||
ClientId: h.streamId,
|
||||
Input: staleRangeRequest(key, withPrefix, 0, revision),
|
||||
Input: staleRangeRequest(key, keyEnd, 0, revision),
|
||||
Call: start.Nanoseconds(),
|
||||
Output: rangeResponse(resp.Kvs, resp.Count, respRevision),
|
||||
Return: end.Nanoseconds(),
|
||||
@ -244,7 +248,8 @@ func toEtcdOperation(option clientv3.Op) (op EtcdOperation) {
|
||||
case option.IsGet():
|
||||
op.Type = RangeOperation
|
||||
op.Range = RangeOptions{
|
||||
Key: string(option.KeyBytes()),
|
||||
Start: string(option.KeyBytes()),
|
||||
End: string(option.RangeBytes()),
|
||||
}
|
||||
case option.IsPut():
|
||||
op.Type = PutOperation
|
||||
@ -342,19 +347,42 @@ func (h *AppendableHistory) appendFailed(request EtcdRequest, call int64, err er
|
||||
}
|
||||
|
||||
func getRequest(key string) EtcdRequest {
|
||||
return rangeRequest(key, false, 0)
|
||||
return rangeRequest(key, "", 0)
|
||||
}
|
||||
|
||||
func staleGetRequest(key string, revision int64) EtcdRequest {
|
||||
return staleRangeRequest(key, false, 0, revision)
|
||||
return staleRangeRequest(key, "", 0, revision)
|
||||
}
|
||||
|
||||
func rangeRequest(key string, withPrefix bool, limit int64) EtcdRequest {
|
||||
return staleRangeRequest(key, withPrefix, limit, 0)
|
||||
func rangeRequest(start, end string, limit int64) EtcdRequest {
|
||||
return staleRangeRequest(start, end, limit, 0)
|
||||
}
|
||||
|
||||
func staleRangeRequest(key string, withPrefix bool, limit, revision int64) EtcdRequest {
|
||||
return EtcdRequest{Type: Range, Range: &RangeRequest{RangeOptions: RangeOptions{Key: key, WithPrefix: withPrefix, Limit: limit}, Revision: revision}}
|
||||
func listRequest(key string, limit int64) EtcdRequest {
|
||||
return staleListRequest(key, limit, 0)
|
||||
}
|
||||
|
||||
func staleListRequest(key string, limit, revision int64) EtcdRequest {
|
||||
return staleRangeRequest(key, prefixEnd(key), limit, revision)
|
||||
}
|
||||
|
||||
// prefixEnd gets the range end of the prefix.
|
||||
// Notice: Keep in sync with /client/v3/op.go getPrefix function.
|
||||
func prefixEnd(key string) string {
|
||||
end := make([]byte, len(key))
|
||||
copy(end, key)
|
||||
for i := len(end) - 1; i >= 0; i-- {
|
||||
if end[i] < 0xff {
|
||||
end[i] = end[i] + 1
|
||||
end = end[:i+1]
|
||||
return string(end)
|
||||
}
|
||||
}
|
||||
return "\x00"
|
||||
}
|
||||
|
||||
func staleRangeRequest(start, end string, limit, revision int64) EtcdRequest {
|
||||
return EtcdRequest{Type: Range, Range: &RangeRequest{RangeOptions: RangeOptions{Start: start, End: end, Limit: limit}, Revision: revision}}
|
||||
}
|
||||
|
||||
func emptyGetResponse(revision int64) MaybeEtcdResponse {
|
||||
|
@ -32,7 +32,7 @@ func TestModelNonDeterministic(t *testing.T) {
|
||||
operations: []testOperation{
|
||||
{req: putRequest("key1", "1"), resp: failedResponse(errors.New("failed"))},
|
||||
{req: putRequest("key2", "2"), resp: putResponse(3)},
|
||||
{req: rangeRequest("key", true, 0), resp: rangeResponse([]*mvccpb.KeyValue{{Key: []byte("key1"), Value: []byte("1"), ModRevision: 2}, {Key: []byte("key2"), Value: []byte("2"), ModRevision: 3}}, 2, 3)},
|
||||
{req: listRequest("key", 0), resp: rangeResponse([]*mvccpb.KeyValue{{Key: []byte("key1"), Value: []byte("1"), ModRevision: 2}, {Key: []byte("key2"), Value: []byte("2"), ModRevision: 3}}, 2, 3)},
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -40,7 +40,7 @@ func TestModelNonDeterministic(t *testing.T) {
|
||||
operations: []testOperation{
|
||||
{req: putRequest("key1", "1"), resp: failedResponse(errors.New("failed"))},
|
||||
{req: putRequest("key2", "2"), resp: putResponse(2)},
|
||||
{req: rangeRequest("key", true, 0), resp: rangeResponse([]*mvccpb.KeyValue{{Key: []byte("key2"), Value: []byte("2"), ModRevision: 2}}, 1, 2)},
|
||||
{req: listRequest("key", 0), resp: rangeResponse([]*mvccpb.KeyValue{{Key: []byte("key2"), Value: []byte("2"), ModRevision: 2}}, 1, 2)},
|
||||
},
|
||||
},
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user