mirror of
https://github.com/etcd-io/etcd.git
synced 2024-09-27 06:25:44 +00:00
tests/robustness: Implement Range limit and count
Signed-off-by: Marek Siarkowicz <siarkowicz@google.com>
This commit is contained in:
parent
249c0d71d4
commit
7c68be4cf3
@ -89,6 +89,9 @@ func describeEtcdOperation(op EtcdOperation) string {
|
|||||||
switch op.Type {
|
switch op.Type {
|
||||||
case Range:
|
case Range:
|
||||||
if op.WithPrefix {
|
if op.WithPrefix {
|
||||||
|
if op.Limit != 0 {
|
||||||
|
return fmt.Sprintf("range(%q, limit=%d)", op.Key, op.Limit)
|
||||||
|
}
|
||||||
return fmt.Sprintf("range(%q)", op.Key)
|
return fmt.Sprintf("range(%q)", op.Key)
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("get(%q)", op.Key)
|
return fmt.Sprintf("get(%q)", op.Key)
|
||||||
@ -112,7 +115,7 @@ func describeEtcdOperationResponse(req EtcdOperation, resp EtcdOperationResult)
|
|||||||
for i, kv := range resp.KVs {
|
for i, kv := range resp.KVs {
|
||||||
kvs[i] = describeValueOrHash(kv.Value)
|
kvs[i] = describeValueOrHash(kv.Value)
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("[%s]", strings.Join(kvs, ","))
|
return fmt.Sprintf("[%s], count: %d", strings.Join(kvs, ","), resp.Count)
|
||||||
} else {
|
} else {
|
||||||
if len(resp.KVs) == 0 {
|
if len(resp.KVs) == 0 {
|
||||||
return "nil"
|
return "nil"
|
||||||
|
@ -105,19 +105,24 @@ func TestModelDescribe(t *testing.T) {
|
|||||||
expectDescribe: `defragment() -> ok, rev: 10`,
|
expectDescribe: `defragment() -> ok, rev: 10`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
req: rangeRequest("key11", true),
|
req: rangeRequest("key11", true, 0),
|
||||||
resp: rangeResponse(nil, 11),
|
resp: rangeResponse(nil, 0, 11),
|
||||||
expectDescribe: `range("key11") -> [], rev: 11`,
|
expectDescribe: `range("key11") -> [], count: 0, rev: 11`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
req: rangeRequest("key12", true),
|
req: rangeRequest("key12", true, 0),
|
||||||
resp: rangeResponse([]*mvccpb.KeyValue{{Value: []byte("12")}}, 12),
|
resp: rangeResponse([]*mvccpb.KeyValue{{Value: []byte("12")}}, 2, 12),
|
||||||
expectDescribe: `range("key12") -> ["12"], rev: 12`,
|
expectDescribe: `range("key12") -> ["12"], count: 2, rev: 12`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
req: rangeRequest("key13", true),
|
req: rangeRequest("key13", true, 0),
|
||||||
resp: rangeResponse([]*mvccpb.KeyValue{{Value: []byte("01234567890123456789")}}, 13),
|
resp: rangeResponse([]*mvccpb.KeyValue{{Value: []byte("01234567890123456789")}}, 1, 13),
|
||||||
expectDescribe: `range("key13") -> [hash: 2945867837], rev: 13`,
|
expectDescribe: `range("key13") -> [hash: 2945867837], count: 1, rev: 13`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
req: rangeRequest("key14", true, 14),
|
||||||
|
resp: rangeResponse(nil, 0, 14),
|
||||||
|
expectDescribe: `range("key14", limit=14) -> [], count: 0, rev: 14`,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, tc := range tcs {
|
for _, tc := range tcs {
|
||||||
|
@ -146,14 +146,20 @@ func (s etcdState) step(request EtcdRequest) (etcdState, EtcdResponse) {
|
|||||||
KVs: []KeyValue{},
|
KVs: []KeyValue{},
|
||||||
}
|
}
|
||||||
if op.WithPrefix {
|
if op.WithPrefix {
|
||||||
|
var count int64
|
||||||
for k, v := range s.KeyValues {
|
for k, v := range s.KeyValues {
|
||||||
if strings.HasPrefix(k, op.Key) {
|
if strings.HasPrefix(k, op.Key) {
|
||||||
opResp[i].KVs = append(opResp[i].KVs, KeyValue{Key: k, ValueRevision: v})
|
opResp[i].KVs = append(opResp[i].KVs, KeyValue{Key: k, ValueRevision: v})
|
||||||
|
count += 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sort.Slice(opResp[i].KVs, func(j, k int) bool {
|
sort.Slice(opResp[i].KVs, func(j, k int) bool {
|
||||||
return opResp[i].KVs[j].Key < opResp[i].KVs[k].Key
|
return opResp[i].KVs[j].Key < opResp[i].KVs[k].Key
|
||||||
})
|
})
|
||||||
|
if op.Limit != 0 && count > op.Limit {
|
||||||
|
opResp[i].KVs = opResp[i].KVs[:op.Limit]
|
||||||
|
}
|
||||||
|
opResp[i].Count = count
|
||||||
} else {
|
} else {
|
||||||
value, ok := s.KeyValues[op.Key]
|
value, ok := s.KeyValues[op.Key]
|
||||||
if ok {
|
if ok {
|
||||||
@ -161,6 +167,7 @@ func (s etcdState) step(request EtcdRequest) (etcdState, EtcdResponse) {
|
|||||||
Key: op.Key,
|
Key: op.Key,
|
||||||
ValueRevision: value,
|
ValueRevision: value,
|
||||||
})
|
})
|
||||||
|
opResp[i].Count = 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case Put:
|
case Put:
|
||||||
@ -270,6 +277,7 @@ type EtcdOperation struct {
|
|||||||
Type OperationType
|
Type OperationType
|
||||||
Key string
|
Key string
|
||||||
WithPrefix bool
|
WithPrefix bool
|
||||||
|
Limit int64
|
||||||
Value ValueOrHash
|
Value ValueOrHash
|
||||||
LeaseID int64
|
LeaseID int64
|
||||||
}
|
}
|
||||||
@ -303,6 +311,7 @@ type DefragmentResponse struct{}
|
|||||||
|
|
||||||
type EtcdOperationResult struct {
|
type EtcdOperationResult struct {
|
||||||
KVs []KeyValue
|
KVs []KeyValue
|
||||||
|
Count int64
|
||||||
Deleted int64
|
Deleted int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,15 +40,15 @@ func TestModelBase(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "First Range can start from non-empty value and non-zero revision",
|
name: "First Range can start from non-empty value and non-zero revision",
|
||||||
operations: []testOperation{
|
operations: []testOperation{
|
||||||
{req: rangeRequest("key", true), resp: rangeResponse([]*mvccpb.KeyValue{{Key: []byte("key"), Value: []byte("1")}}, 42).EtcdResponse},
|
{req: rangeRequest("key", true, 0), resp: rangeResponse([]*mvccpb.KeyValue{{Key: []byte("key"), Value: []byte("1")}}, 1, 42).EtcdResponse},
|
||||||
{req: rangeRequest("key", true), resp: rangeResponse([]*mvccpb.KeyValue{{Key: []byte("key"), Value: []byte("1")}}, 42).EtcdResponse},
|
{req: rangeRequest("key", true, 0), resp: rangeResponse([]*mvccpb.KeyValue{{Key: []byte("key"), Value: []byte("1")}}, 1, 42).EtcdResponse},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "First Range can start from non-zero revision",
|
name: "First Range can start from non-zero revision",
|
||||||
operations: []testOperation{
|
operations: []testOperation{
|
||||||
{req: rangeRequest("key", true), resp: rangeResponse(nil, 1).EtcdResponse},
|
{req: rangeRequest("key", true, 0), resp: rangeResponse(nil, 0, 1).EtcdResponse},
|
||||||
{req: rangeRequest("key", true), resp: rangeResponse(nil, 1).EtcdResponse},
|
{req: rangeRequest("key", true, 0), resp: rangeResponse(nil, 0, 1).EtcdResponse},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -89,18 +89,55 @@ func TestModelBase(t *testing.T) {
|
|||||||
operations: []testOperation{
|
operations: []testOperation{
|
||||||
{req: putRequest("key1", "1"), resp: putResponse(1).EtcdResponse},
|
{req: putRequest("key1", "1"), resp: putResponse(1).EtcdResponse},
|
||||||
{req: putRequest("key2", "2"), resp: putResponse(2).EtcdResponse},
|
{req: putRequest("key2", "2"), resp: putResponse(2).EtcdResponse},
|
||||||
{req: rangeRequest("key", true), resp: rangeResponse([]*mvccpb.KeyValue{{Key: []byte("key1"), Value: []byte("1"), ModRevision: 1}, {Key: []byte("key2"), Value: []byte("2"), ModRevision: 2}}, 2).EtcdResponse},
|
{req: rangeRequest("key", true, 0), resp: rangeResponse([]*mvccpb.KeyValue{{Key: []byte("key1"), Value: []byte("1"), ModRevision: 1}, {Key: []byte("key2"), Value: []byte("2"), ModRevision: 2}}, 2, 2).EtcdResponse},
|
||||||
{req: rangeRequest("key", true), resp: rangeResponse([]*mvccpb.KeyValue{{Key: []byte("key1"), Value: []byte("1"), ModRevision: 1}, {Key: []byte("key2"), Value: []byte("2"), ModRevision: 2}}, 2).EtcdResponse},
|
{req: rangeRequest("key", true, 0), resp: rangeResponse([]*mvccpb.KeyValue{{Key: []byte("key1"), Value: []byte("1"), ModRevision: 1}, {Key: []byte("key2"), Value: []byte("2"), ModRevision: 2}}, 2, 2).EtcdResponse},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Range limit should reduce number of kvs, but maintain count",
|
||||||
|
operations: []testOperation{
|
||||||
|
{req: rangeRequest("key", true, 0), resp: rangeResponse([]*mvccpb.KeyValue{
|
||||||
|
{Key: []byte("key1"), Value: []byte("1"), ModRevision: 1},
|
||||||
|
{Key: []byte("key2"), Value: []byte("2"), ModRevision: 2},
|
||||||
|
{Key: []byte("key3"), Value: []byte("3"), ModRevision: 3},
|
||||||
|
}, 3, 3).EtcdResponse},
|
||||||
|
{req: rangeRequest("key", true, 4), resp: rangeResponse([]*mvccpb.KeyValue{
|
||||||
|
{Key: []byte("key1"), Value: []byte("1"), ModRevision: 1},
|
||||||
|
{Key: []byte("key2"), Value: []byte("2"), ModRevision: 2},
|
||||||
|
{Key: []byte("key3"), Value: []byte("3"), ModRevision: 3},
|
||||||
|
}, 3, 3).EtcdResponse},
|
||||||
|
{req: rangeRequest("key", true, 3), resp: rangeResponse([]*mvccpb.KeyValue{
|
||||||
|
{Key: []byte("key1"), Value: []byte("1"), ModRevision: 1},
|
||||||
|
{Key: []byte("key2"), Value: []byte("2"), ModRevision: 2},
|
||||||
|
{Key: []byte("key3"), Value: []byte("3"), ModRevision: 3},
|
||||||
|
}, 3, 3).EtcdResponse},
|
||||||
|
{req: rangeRequest("key", true, 2), resp: rangeResponse([]*mvccpb.KeyValue{
|
||||||
|
{Key: []byte("key1"), Value: []byte("1"), ModRevision: 1},
|
||||||
|
{Key: []byte("key2"), Value: []byte("2"), ModRevision: 2},
|
||||||
|
}, 3, 3).EtcdResponse},
|
||||||
|
{req: rangeRequest("key", true, 1), resp: rangeResponse([]*mvccpb.KeyValue{
|
||||||
|
{Key: []byte("key1"), Value: []byte("1"), ModRevision: 1},
|
||||||
|
}, 3, 3).EtcdResponse},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Range response should be ordered by key",
|
name: "Range response should be ordered by key",
|
||||||
operations: []testOperation{
|
operations: []testOperation{
|
||||||
{req: rangeRequest("key", true), resp: rangeResponse([]*mvccpb.KeyValue{
|
{req: rangeRequest("key", true, 0), resp: rangeResponse([]*mvccpb.KeyValue{
|
||||||
{Key: []byte("key1"), Value: []byte("2"), ModRevision: 3},
|
{Key: []byte("key1"), Value: []byte("2"), ModRevision: 3},
|
||||||
{Key: []byte("key2"), Value: []byte("1"), ModRevision: 2},
|
{Key: []byte("key2"), Value: []byte("1"), ModRevision: 2},
|
||||||
{Key: []byte("key3"), Value: []byte("3"), ModRevision: 1},
|
{Key: []byte("key3"), Value: []byte("3"), ModRevision: 1},
|
||||||
}, 3).EtcdResponse},
|
}, 3, 3).EtcdResponse},
|
||||||
|
{req: rangeRequest("key", true, 0), resp: rangeResponse([]*mvccpb.KeyValue{
|
||||||
|
{Key: []byte("key2"), Value: []byte("1"), ModRevision: 2},
|
||||||
|
{Key: []byte("key1"), Value: []byte("2"), ModRevision: 3},
|
||||||
|
{Key: []byte("key3"), Value: []byte("3"), ModRevision: 1},
|
||||||
|
}, 3, 3).EtcdResponse, failure: true},
|
||||||
|
{req: rangeRequest("key", true, 0), resp: rangeResponse([]*mvccpb.KeyValue{
|
||||||
|
{Key: []byte("key3"), Value: []byte("3"), ModRevision: 1},
|
||||||
|
{Key: []byte("key2"), Value: []byte("1"), ModRevision: 2},
|
||||||
|
{Key: []byte("key1"), Value: []byte("2"), ModRevision: 3},
|
||||||
|
}, 3, 3).EtcdResponse, failure: true},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -72,9 +72,9 @@ func (h *AppendableHistory) AppendRange(key string, withPrefix bool, start, end
|
|||||||
}
|
}
|
||||||
h.successful = append(h.successful, porcupine.Operation{
|
h.successful = append(h.successful, porcupine.Operation{
|
||||||
ClientId: h.id,
|
ClientId: h.id,
|
||||||
Input: rangeRequest(key, withPrefix),
|
Input: rangeRequest(key, withPrefix, 0),
|
||||||
Call: start.Nanoseconds(),
|
Call: start.Nanoseconds(),
|
||||||
Output: rangeResponse(resp.Kvs, revision),
|
Output: rangeResponse(resp.Kvs, resp.Count, revision),
|
||||||
Return: end.Nanoseconds(),
|
Return: end.Nanoseconds(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -299,7 +299,8 @@ func toEtcdOperationResult(resp *etcdserverpb.ResponseOp) EtcdOperationResult {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return EtcdOperationResult{
|
return EtcdOperationResult{
|
||||||
KVs: kvs,
|
KVs: kvs,
|
||||||
|
Count: getResp.Count,
|
||||||
}
|
}
|
||||||
case resp.GetResponsePut() != nil:
|
case resp.GetResponsePut() != nil:
|
||||||
return EtcdOperationResult{}
|
return EtcdOperationResult{}
|
||||||
@ -345,22 +346,22 @@ func (h *AppendableHistory) appendFailed(request EtcdRequest, start time.Duratio
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getRequest(key string) EtcdRequest {
|
func getRequest(key string) EtcdRequest {
|
||||||
return rangeRequest(key, false)
|
return rangeRequest(key, false, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func rangeRequest(key string, withPrefix bool) EtcdRequest {
|
func rangeRequest(key string, withPrefix bool, limit int64) EtcdRequest {
|
||||||
return EtcdRequest{Type: Txn, Txn: &TxnRequest{Ops: []EtcdOperation{{Type: Range, Key: key, WithPrefix: withPrefix}}}}
|
return EtcdRequest{Type: Txn, Txn: &TxnRequest{Ops: []EtcdOperation{{Type: Range, Key: key, WithPrefix: withPrefix, Limit: limit}}}}
|
||||||
}
|
}
|
||||||
|
|
||||||
func emptyGetResponse(revision int64) EtcdNonDeterministicResponse {
|
func emptyGetResponse(revision int64) EtcdNonDeterministicResponse {
|
||||||
return rangeResponse([]*mvccpb.KeyValue{}, revision)
|
return rangeResponse([]*mvccpb.KeyValue{}, 0, revision)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getResponse(key, value string, modRevision, revision int64) EtcdNonDeterministicResponse {
|
func getResponse(key, value string, modRevision, revision int64) EtcdNonDeterministicResponse {
|
||||||
return rangeResponse([]*mvccpb.KeyValue{{Key: []byte(key), Value: []byte(value), ModRevision: modRevision}}, revision)
|
return rangeResponse([]*mvccpb.KeyValue{{Key: []byte(key), Value: []byte(value), ModRevision: modRevision}}, 1, revision)
|
||||||
}
|
}
|
||||||
|
|
||||||
func rangeResponse(kvs []*mvccpb.KeyValue, revision int64) EtcdNonDeterministicResponse {
|
func rangeResponse(kvs []*mvccpb.KeyValue, count int64, revision int64) EtcdNonDeterministicResponse {
|
||||||
result := EtcdOperationResult{KVs: make([]KeyValue, len(kvs))}
|
result := EtcdOperationResult{KVs: make([]KeyValue, len(kvs))}
|
||||||
|
|
||||||
for i, kv := range kvs {
|
for i, kv := range kvs {
|
||||||
@ -371,6 +372,7 @@ func rangeResponse(kvs []*mvccpb.KeyValue, revision int64) EtcdNonDeterministicR
|
|||||||
ModRevision: kv.ModRevision,
|
ModRevision: kv.ModRevision,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
result.Count = count
|
||||||
}
|
}
|
||||||
return EtcdNonDeterministicResponse{EtcdResponse: EtcdResponse{Txn: &TxnResponse{OpsResult: []EtcdOperationResult{result}}, Revision: revision}}
|
return EtcdNonDeterministicResponse{EtcdResponse: EtcdResponse{Txn: &TxnResponse{OpsResult: []EtcdOperationResult{result}}, Revision: revision}}
|
||||||
}
|
}
|
||||||
|
@ -43,15 +43,15 @@ func TestModelNonDeterministic(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "First Range can start from non-empty value and non-zero revision",
|
name: "First Range can start from non-empty value and non-zero revision",
|
||||||
operations: []testOperation{
|
operations: []testOperation{
|
||||||
{req: rangeRequest("key", true), resp: rangeResponse([]*mvccpb.KeyValue{{Key: []byte("key"), Value: []byte("1")}}, 42)},
|
{req: rangeRequest("key", true, 0), resp: rangeResponse([]*mvccpb.KeyValue{{Key: []byte("key"), Value: []byte("1")}}, 1, 42)},
|
||||||
{req: rangeRequest("key", true), resp: rangeResponse([]*mvccpb.KeyValue{{Key: []byte("key"), Value: []byte("1")}}, 42)},
|
{req: rangeRequest("key", true, 0), resp: rangeResponse([]*mvccpb.KeyValue{{Key: []byte("key"), Value: []byte("1")}}, 1, 42)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "First Range can start from non-zero revision",
|
name: "First Range can start from non-zero revision",
|
||||||
operations: []testOperation{
|
operations: []testOperation{
|
||||||
{req: rangeRequest("key", true), resp: rangeResponse(nil, 1)},
|
{req: rangeRequest("key", true, 0), resp: rangeResponse(nil, 1, 1)},
|
||||||
{req: rangeRequest("key", true), resp: rangeResponse(nil, 1)},
|
{req: rangeRequest("key", true, 0), resp: rangeResponse(nil, 1, 1)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -92,18 +92,18 @@ func TestModelNonDeterministic(t *testing.T) {
|
|||||||
operations: []testOperation{
|
operations: []testOperation{
|
||||||
{req: putRequest("key1", "1"), resp: putResponse(1)},
|
{req: putRequest("key1", "1"), resp: putResponse(1)},
|
||||||
{req: putRequest("key2", "2"), resp: putResponse(2)},
|
{req: putRequest("key2", "2"), resp: putResponse(2)},
|
||||||
{req: rangeRequest("key", true), resp: rangeResponse([]*mvccpb.KeyValue{{Key: []byte("key1"), Value: []byte("1"), ModRevision: 1}, {Key: []byte("key2"), Value: []byte("2"), ModRevision: 2}}, 2)},
|
{req: rangeRequest("key", true, 0), resp: rangeResponse([]*mvccpb.KeyValue{{Key: []byte("key1"), Value: []byte("1"), ModRevision: 1}, {Key: []byte("key2"), Value: []byte("2"), ModRevision: 2}}, 2, 2)},
|
||||||
{req: rangeRequest("key", true), resp: rangeResponse([]*mvccpb.KeyValue{{Key: []byte("key1"), Value: []byte("1"), ModRevision: 1}, {Key: []byte("key2"), Value: []byte("2"), ModRevision: 2}}, 2)},
|
{req: rangeRequest("key", true, 0), resp: rangeResponse([]*mvccpb.KeyValue{{Key: []byte("key1"), Value: []byte("1"), ModRevision: 1}, {Key: []byte("key2"), Value: []byte("2"), ModRevision: 2}}, 2, 2)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Range response should be ordered by key",
|
name: "Range response should be ordered by key",
|
||||||
operations: []testOperation{
|
operations: []testOperation{
|
||||||
{req: rangeRequest("key", true), resp: rangeResponse([]*mvccpb.KeyValue{
|
{req: rangeRequest("key", true, 0), resp: rangeResponse([]*mvccpb.KeyValue{
|
||||||
{Key: []byte("key1"), Value: []byte("2"), ModRevision: 3},
|
{Key: []byte("key1"), Value: []byte("2"), ModRevision: 3},
|
||||||
{Key: []byte("key2"), Value: []byte("1"), ModRevision: 2},
|
{Key: []byte("key2"), Value: []byte("1"), ModRevision: 2},
|
||||||
{Key: []byte("key3"), Value: []byte("3"), ModRevision: 1},
|
{Key: []byte("key3"), Value: []byte("3"), ModRevision: 1},
|
||||||
}, 3)},
|
}, 3, 3)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user