tests/robustness: Implement Range limit and count

Signed-off-by: Marek Siarkowicz <siarkowicz@google.com>
dependabot/go_modules/github.com/prometheus/procfs-0.11.0
Marek Siarkowicz 2023-05-07 09:31:47 +02:00
parent 249c0d71d4
commit 7c68be4cf3
6 changed files with 91 additions and 35 deletions

View File

@ -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"

View File

@ -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 {

View File

@ -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
}

View File

@ -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},
},
},
{

View File

@ -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}}
}

View File

@ -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)},
},
},
{