mirror of
https://github.com/etcd-io/etcd.git
synced 2024-09-27 06:25:44 +00:00
Merge pull request #15840 from serathius/robustness-limit-count
tests/robustness: Implement Range limit and count
This commit is contained in:
commit
767b59f0b0
@ -89,6 +89,9 @@ func describeEtcdOperation(op EtcdOperation) string {
|
||||
switch op.Type {
|
||||
case Range:
|
||||
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("get(%q)", op.Key)
|
||||
@ -112,7 +115,7 @@ func describeEtcdOperationResponse(req EtcdOperation, resp EtcdOperationResult)
|
||||
for i, kv := range resp.KVs {
|
||||
kvs[i] = describeValueOrHash(kv.Value)
|
||||
}
|
||||
return fmt.Sprintf("[%s]", strings.Join(kvs, ","))
|
||||
return fmt.Sprintf("[%s], count: %d", strings.Join(kvs, ","), resp.Count)
|
||||
} else {
|
||||
if len(resp.KVs) == 0 {
|
||||
return "nil"
|
||||
|
@ -105,19 +105,24 @@ func TestModelDescribe(t *testing.T) {
|
||||
expectDescribe: `defragment() -> ok, rev: 10`,
|
||||
},
|
||||
{
|
||||
req: rangeRequest("key11", true),
|
||||
resp: rangeResponse(nil, 11),
|
||||
expectDescribe: `range("key11") -> [], rev: 11`,
|
||||
req: rangeRequest("key11", true, 0),
|
||||
resp: rangeResponse(nil, 0, 11),
|
||||
expectDescribe: `range("key11") -> [], count: 0, rev: 11`,
|
||||
},
|
||||
{
|
||||
req: rangeRequest("key12", true),
|
||||
resp: rangeResponse([]*mvccpb.KeyValue{{Value: []byte("12")}}, 12),
|
||||
expectDescribe: `range("key12") -> ["12"], rev: 12`,
|
||||
req: rangeRequest("key12", true, 0),
|
||||
resp: rangeResponse([]*mvccpb.KeyValue{{Value: []byte("12")}}, 2, 12),
|
||||
expectDescribe: `range("key12") -> ["12"], count: 2, rev: 12`,
|
||||
},
|
||||
{
|
||||
req: rangeRequest("key13", true),
|
||||
resp: rangeResponse([]*mvccpb.KeyValue{{Value: []byte("01234567890123456789")}}, 13),
|
||||
expectDescribe: `range("key13") -> [hash: 2945867837], rev: 13`,
|
||||
req: rangeRequest("key13", true, 0),
|
||||
resp: rangeResponse([]*mvccpb.KeyValue{{Value: []byte("01234567890123456789")}}, 1, 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 {
|
||||
|
@ -146,14 +146,20 @@ func (s etcdState) step(request EtcdRequest) (etcdState, EtcdResponse) {
|
||||
KVs: []KeyValue{},
|
||||
}
|
||||
if op.WithPrefix {
|
||||
var count int64
|
||||
for k, v := range s.KeyValues {
|
||||
if strings.HasPrefix(k, op.Key) {
|
||||
opResp[i].KVs = append(opResp[i].KVs, KeyValue{Key: k, ValueRevision: v})
|
||||
count += 1
|
||||
}
|
||||
}
|
||||
sort.Slice(opResp[i].KVs, func(j, k int) bool {
|
||||
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 {
|
||||
value, ok := s.KeyValues[op.Key]
|
||||
if ok {
|
||||
@ -161,6 +167,7 @@ func (s etcdState) step(request EtcdRequest) (etcdState, EtcdResponse) {
|
||||
Key: op.Key,
|
||||
ValueRevision: value,
|
||||
})
|
||||
opResp[i].Count = 1
|
||||
}
|
||||
}
|
||||
case Put:
|
||||
@ -270,6 +277,7 @@ type EtcdOperation struct {
|
||||
Type OperationType
|
||||
Key string
|
||||
WithPrefix bool
|
||||
Limit int64
|
||||
Value ValueOrHash
|
||||
LeaseID int64
|
||||
}
|
||||
@ -303,6 +311,7 @@ type DefragmentResponse struct{}
|
||||
|
||||
type EtcdOperationResult struct {
|
||||
KVs []KeyValue
|
||||
Count 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",
|
||||
operations: []testOperation{
|
||||
{req: rangeRequest("key", true), resp: rangeResponse([]*mvccpb.KeyValue{{Key: []byte("key"), Value: []byte("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},
|
||||
{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",
|
||||
operations: []testOperation{
|
||||
{req: rangeRequest("key", true), resp: rangeResponse(nil, 1).EtcdResponse},
|
||||
{req: rangeRequest("key", true), resp: rangeResponse(nil, 1).EtcdResponse},
|
||||
{req: rangeRequest("key", true, 0), resp: rangeResponse(nil, 0, 1).EtcdResponse},
|
||||
{req: rangeRequest("key", true, 0), resp: rangeResponse(nil, 0, 1).EtcdResponse},
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -89,18 +89,55 @@ func TestModelBase(t *testing.T) {
|
||||
operations: []testOperation{
|
||||
{req: putRequest("key1", "1"), resp: putResponse(1).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), 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, 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",
|
||||
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("key2"), Value: []byte("1"), ModRevision: 2},
|
||||
{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{
|
||||
ClientId: h.id,
|
||||
Input: rangeRequest(key, withPrefix),
|
||||
Input: rangeRequest(key, withPrefix, 0),
|
||||
Call: start.Nanoseconds(),
|
||||
Output: rangeResponse(resp.Kvs, revision),
|
||||
Output: rangeResponse(resp.Kvs, resp.Count, revision),
|
||||
Return: end.Nanoseconds(),
|
||||
})
|
||||
}
|
||||
@ -299,7 +299,8 @@ func toEtcdOperationResult(resp *etcdserverpb.ResponseOp) EtcdOperationResult {
|
||||
}
|
||||
}
|
||||
return EtcdOperationResult{
|
||||
KVs: kvs,
|
||||
KVs: kvs,
|
||||
Count: getResp.Count,
|
||||
}
|
||||
case resp.GetResponsePut() != nil:
|
||||
return EtcdOperationResult{}
|
||||
@ -345,22 +346,22 @@ func (h *AppendableHistory) appendFailed(request EtcdRequest, start time.Duratio
|
||||
}
|
||||
|
||||
func getRequest(key string) EtcdRequest {
|
||||
return rangeRequest(key, false)
|
||||
return rangeRequest(key, false, 0)
|
||||
}
|
||||
|
||||
func rangeRequest(key string, withPrefix bool) EtcdRequest {
|
||||
return EtcdRequest{Type: Txn, Txn: &TxnRequest{Ops: []EtcdOperation{{Type: Range, Key: key, WithPrefix: withPrefix}}}}
|
||||
func rangeRequest(key string, withPrefix bool, limit int64) EtcdRequest {
|
||||
return EtcdRequest{Type: Txn, Txn: &TxnRequest{Ops: []EtcdOperation{{Type: Range, Key: key, WithPrefix: withPrefix, Limit: limit}}}}
|
||||
}
|
||||
|
||||
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 {
|
||||
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))}
|
||||
|
||||
for i, kv := range kvs {
|
||||
@ -371,6 +372,7 @@ func rangeResponse(kvs []*mvccpb.KeyValue, revision int64) EtcdNonDeterministicR
|
||||
ModRevision: kv.ModRevision,
|
||||
},
|
||||
}
|
||||
result.Count = count
|
||||
}
|
||||
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",
|
||||
operations: []testOperation{
|
||||
{req: rangeRequest("key", true), resp: rangeResponse([]*mvccpb.KeyValue{{Key: []byte("key"), Value: []byte("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)},
|
||||
{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",
|
||||
operations: []testOperation{
|
||||
{req: rangeRequest("key", true), resp: rangeResponse(nil, 1)},
|
||||
{req: rangeRequest("key", true), resp: rangeResponse(nil, 1)},
|
||||
{req: rangeRequest("key", true, 0), resp: rangeResponse(nil, 1, 1)},
|
||||
{req: rangeRequest("key", true, 0), resp: rangeResponse(nil, 1, 1)},
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -92,18 +92,18 @@ func TestModelNonDeterministic(t *testing.T) {
|
||||
operations: []testOperation{
|
||||
{req: putRequest("key1", "1"), resp: putResponse(1)},
|
||||
{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), 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, 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",
|
||||
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("key2"), Value: []byte("1"), ModRevision: 2},
|
||||
{Key: []byte("key3"), Value: []byte("3"), ModRevision: 1},
|
||||
}, 3)},
|
||||
}, 3, 3)},
|
||||
},
|
||||
},
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user