Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Version field to the robustness model
Browse files Browse the repository at this point in the history
It helps perform Txn before Compact in the same way
as it is done in Kubernetes.

Signed-off-by: Aleksander Mistewicz <[email protected]>
AwesomePatrol committed Jan 22, 2025

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
1 parent 4e77d69 commit 4fcd7d0
Showing 10 changed files with 99 additions and 52 deletions.
1 change: 1 addition & 0 deletions tests/robustness/client/client.go
Original file line number Diff line number Diff line change
@@ -356,6 +356,7 @@ func toWatchEvent(event clientv3.Event) (watch model.WatchEvent) {
watch.PrevValue = &model.ValueRevision{
Value: model.ToValueOrHash(string(event.PrevKv.Value)),
ModRevision: event.PrevKv.ModRevision,
Version: event.PrevKv.Version,
}
}
watch.IsCreate = event.IsCreate()
11 changes: 9 additions & 2 deletions tests/robustness/model/describe.go
Original file line number Diff line number Diff line change
@@ -91,6 +91,9 @@ func describeGuaranteedTxn(txn *TxnRequest) string {
if txn.Conditions[0].Key != txn.OperationsOnSuccess[0].Put.Key || (len(txn.OperationsOnFailure) == 1 && txn.Conditions[0].Key != txn.OperationsOnFailure[0].Range.Start) {
return ""
}
if txn.Conditions[0].ExpectedVersion > 0 {
return ""
}
if txn.Conditions[0].ExpectedRevision == 0 {
return fmt.Sprintf("guaranteedCreate(%q, %s)", txn.Conditions[0].Key, describeValueOrHash(txn.OperationsOnSuccess[0].Put.Value))
}
@@ -106,8 +109,12 @@ func describeGuaranteedTxn(txn *TxnRequest) string {

func describeEtcdConditions(conds []EtcdCondition) string {
opsDescription := make([]string, len(conds))
for i := range conds {
opsDescription[i] = fmt.Sprintf("mod_rev(%s)==%d", conds[i].Key, conds[i].ExpectedRevision)
for i, cond := range conds {
if cond.ExpectedVersion > 0 {
opsDescription[i] = fmt.Sprintf("ver(%s)==%d", cond.Key, cond.ExpectedVersion)
} else {
opsDescription[i] = fmt.Sprintf("mod_rev(%s)==%d", cond.Key, cond.ExpectedRevision)
}
}
return strings.Join(opsDescription, " && ")
}
15 changes: 14 additions & 1 deletion tests/robustness/model/deterministic.go
Original file line number Diff line number Diff line change
@@ -127,7 +127,13 @@ func (s EtcdState) Step(request EtcdRequest) (EtcdState, MaybeEtcdResponse) {
case Txn:
failure := false
for _, cond := range request.Txn.Conditions {
if val := newState.KeyValues[cond.Key]; val.ModRevision != cond.ExpectedRevision {
val := newState.KeyValues[cond.Key]
if cond.ExpectedVersion > 0 {
if val.Version != cond.ExpectedVersion {
failure = true
break
}
} else if val.ModRevision != cond.ExpectedRevision {
failure = true
break
}
@@ -149,9 +155,14 @@ func (s EtcdState) Step(request EtcdRequest) (EtcdState, MaybeEtcdResponse) {
if op.Put.LeaseID != 0 && !leaseExists {
break
}
ver := int64(1)
if val, exists := newState.KeyValues[op.Put.Key]; exists && val.Version > 0 {
ver = val.Version + 1
}
newState.KeyValues[op.Put.Key] = ValueRevision{
Value: op.Put.Value,
ModRevision: newState.Revision + 1,
Version: ver,
}
increaseRevision = true
newState = detachFromOldLease(newState, op.Put.Key)
@@ -326,6 +337,7 @@ type TxnRequest struct {
type EtcdCondition struct {
Key string
ExpectedRevision int64
ExpectedVersion int64
}

type EtcdOperation struct {
@@ -434,6 +446,7 @@ func (el EtcdLease) DeepCopy() EtcdLease {
type ValueRevision struct {
Value ValueOrHash
ModRevision int64
Version int64
}

type ValueOrHash struct {
60 changes: 30 additions & 30 deletions tests/robustness/model/deterministic_test.go
Original file line number Diff line number Diff line change
@@ -82,8 +82,8 @@ var commonTestScenarios = []modelTestCase{
operations: []testOperation{
{req: putRequest("key1", "1"), resp: putResponse(2)},
{req: putRequest("key2", "2"), resp: putResponse(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)},
{req: listRequest("key", 0), resp: rangeResponse([]*mvccpb.KeyValue{{Key: []byte("key1"), Value: []byte("1"), ModRevision: 2, Version: 1}, {Key: []byte("key2"), Value: []byte("2"), ModRevision: 3, Version: 1}}, 2, 3)},
{req: listRequest("key", 0), resp: rangeResponse([]*mvccpb.KeyValue{{Key: []byte("key1"), Value: []byte("1"), ModRevision: 2, Version: 1}, {Key: []byte("key2"), Value: []byte("2"), ModRevision: 3, Version: 1}}, 2, 3)},
},
},
{
@@ -93,26 +93,26 @@ var commonTestScenarios = []modelTestCase{
{req: putRequest("key2", "2"), resp: putResponse(3)},
{req: putRequest("key3", "3"), resp: putResponse(4)},
{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},
{Key: []byte("key1"), Value: []byte("1"), ModRevision: 2, Version: 1},
{Key: []byte("key2"), Value: []byte("2"), ModRevision: 3, Version: 1},
{Key: []byte("key3"), Value: []byte("3"), ModRevision: 4, Version: 1},
}, 3, 4)},
{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},
{Key: []byte("key1"), Value: []byte("1"), ModRevision: 2, Version: 1},
{Key: []byte("key2"), Value: []byte("2"), ModRevision: 3, Version: 1},
{Key: []byte("key3"), Value: []byte("3"), ModRevision: 4, Version: 1},
}, 3, 4)},
{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},
{Key: []byte("key1"), Value: []byte("1"), ModRevision: 2, Version: 1},
{Key: []byte("key2"), Value: []byte("2"), ModRevision: 3, Version: 1},
{Key: []byte("key3"), Value: []byte("3"), ModRevision: 4, Version: 1},
}, 3, 4)},
{req: listRequest("key", 2), resp: rangeResponse([]*mvccpb.KeyValue{
{Key: []byte("key1"), Value: []byte("1"), ModRevision: 2},
{Key: []byte("key2"), Value: []byte("2"), ModRevision: 3},
{Key: []byte("key1"), Value: []byte("1"), ModRevision: 2, Version: 1},
{Key: []byte("key2"), Value: []byte("2"), ModRevision: 3, Version: 1},
}, 3, 4)},
{req: listRequest("key", 1), resp: rangeResponse([]*mvccpb.KeyValue{
{Key: []byte("key1"), Value: []byte("1"), ModRevision: 2},
{Key: []byte("key1"), Value: []byte("1"), ModRevision: 2, Version: 1},
}, 3, 4)},
},
},
@@ -123,19 +123,19 @@ var commonTestScenarios = []modelTestCase{
{req: putRequest("key2", "1"), resp: putResponse(3)},
{req: putRequest("key1", "2"), resp: putResponse(4)},
{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},
{Key: []byte("key1"), Value: []byte("2"), ModRevision: 4, Version: 1},
{Key: []byte("key2"), Value: []byte("1"), ModRevision: 3, Version: 1},
{Key: []byte("key3"), Value: []byte("3"), ModRevision: 2, Version: 1},
}, 3, 4)},
{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},
{Key: []byte("key2"), Value: []byte("1"), ModRevision: 3, Version: 1},
{Key: []byte("key1"), Value: []byte("2"), ModRevision: 4, Version: 1},
{Key: []byte("key3"), Value: []byte("3"), ModRevision: 2, Version: 1},
}, 3, 4), expectFailure: true},
{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},
{Key: []byte("key3"), Value: []byte("3"), ModRevision: 2, Version: 1},
{Key: []byte("key2"), Value: []byte("1"), ModRevision: 3, Version: 1},
{Key: []byte("key1"), Value: []byte("2"), ModRevision: 4, Version: 1},
}, 3, 4), expectFailure: true},
},
},
@@ -146,7 +146,7 @@ var commonTestScenarios = []modelTestCase{
{req: getRequest("key"), resp: getResponse("key", "123456789012345678901", 2, 2), expectFailure: true},
{req: getRequest("key"), resp: getResponse("key", "012345678901234567890", 2, 2)},
{req: putRequest("key", "123456789012345678901"), resp: putResponse(3)},
{req: getRequest("key"), resp: getResponse("key", "123456789012345678901", 3, 3)},
{req: getRequest("key"), resp: getResponseWithVer("key", "123456789012345678901", 3, 2, 3)},
{req: getRequest("key"), resp: getResponse("key", "012345678901234567890", 3, 3), expectFailure: true},
},
},
@@ -228,8 +228,8 @@ var commonTestScenarios = []modelTestCase{
{req: getRequest("key"), resp: getResponse("key", "1", 2, 2), expectFailure: true},
{req: getRequest("key"), resp: getResponse("key", "1", 2, 3), expectFailure: true},
{req: getRequest("key"), resp: getResponse("key", "1", 3, 3), expectFailure: true},
{req: getRequest("key"), resp: getResponse("key", "2", 2, 2), expectFailure: true},
{req: getRequest("key"), resp: getResponse("key", "2", 3, 3)},
{req: getRequest("key"), resp: getResponseWithVer("key", "2", 2, 2, 2), expectFailure: true},
{req: getRequest("key"), resp: getResponseWithVer("key", "2", 3, 2, 3)},
},
},
{
@@ -239,7 +239,7 @@ var commonTestScenarios = []modelTestCase{
{req: compareRevisionAndPutRequest("key1", 0, "2"), resp: compareRevisionAndPutResponse(true, 2)},
{req: compareRevisionAndPutRequest("key1", 0, "3"), resp: compareRevisionAndPutResponse(true, 3), expectFailure: true},
{req: txnRequestSingleOperation(compareRevision("key1", 0), putOperation("key1", "4"), putOperation("key1", "5")), resp: txnPutResponse(false, 3)},
{req: getRequest("key1"), resp: getResponse("key1", "5", 3, 3)},
{req: getRequest("key1"), resp: getResponseWithVer("key1", "5", 3, 2, 3)},
{req: compareRevisionAndPutRequest("key2", 0, "6"), resp: compareRevisionAndPutResponse(true, 4)},
},
},
@@ -291,7 +291,7 @@ var commonTestScenarios = []modelTestCase{
{req: putWithLeaseRequest("key", "2", 1), resp: putResponse(2)},
{req: putRequest("key", "3"), resp: putResponse(3)},
{req: leaseRevokeRequest(1), resp: leaseRevokeResponse(3)},
{req: getRequest("key"), resp: getResponse("key", "3", 3, 3)},
{req: getRequest("key"), resp: getResponseWithVer("key", "3", 3, 2, 3)},
},
},
{
@@ -302,7 +302,7 @@ var commonTestScenarios = []modelTestCase{
{req: putWithLeaseRequest("key", "2", 1), resp: putResponse(2)},
{req: putWithLeaseRequest("key", "3", 2), resp: putResponse(3)},
{req: leaseRevokeRequest(1), resp: leaseRevokeResponse(3)},
{req: getRequest("key"), resp: getResponse("key", "3", 3, 3)},
{req: getRequest("key"), resp: getResponseWithVer("key", "3", 3, 2, 3)},
{req: leaseRevokeRequest(2), resp: leaseRevokeResponse(4)},
{req: getRequest("key"), resp: emptyGetResponse(4)},
},
@@ -313,7 +313,7 @@ var commonTestScenarios = []modelTestCase{
{req: leaseGrantRequest(1), resp: leaseGrantResponse(1)},
{req: putWithLeaseRequest("key", "2", 1), resp: putResponse(2)},
{req: putWithLeaseRequest("key", "3", 1), resp: putResponse(3)},
{req: getRequest("key"), resp: getResponse("key", "3", 3, 3)},
{req: getRequest("key"), resp: getResponseWithVer("key", "3", 3, 2, 3)},
},
},
{
14 changes: 12 additions & 2 deletions tests/robustness/model/history.go
Original file line number Diff line number Diff line change
@@ -183,12 +183,16 @@ func toEtcdCondition(cmp clientv3.Cmp) (cond EtcdCondition) {
switch {
case cmp.Result == etcdserverpb.Compare_EQUAL && cmp.Target == etcdserverpb.Compare_MOD:
cond.Key = string(cmp.KeyBytes())
cond.ExpectedRevision = cmp.TargetUnion.(*etcdserverpb.Compare_ModRevision).ModRevision
case cmp.Result == etcdserverpb.Compare_EQUAL && cmp.Target == etcdserverpb.Compare_CREATE:
cond.Key = string(cmp.KeyBytes())
cond.ExpectedRevision = cmp.TargetUnion.(*etcdserverpb.Compare_ModRevision).ModRevision
case cmp.Result == etcdserverpb.Compare_EQUAL && cmp.Target == etcdserverpb.Compare_VERSION:
cond.ExpectedVersion = cmp.TargetUnion.(*etcdserverpb.Compare_Version).Version
cond.Key = string(cmp.KeyBytes())
default:
panic(fmt.Sprintf("Compare not supported, target: %q, result: %q", cmp.Target, cmp.Result))
}
cond.ExpectedRevision = cmp.TargetUnion.(*etcdserverpb.Compare_ModRevision).ModRevision
return cond
}

@@ -228,6 +232,7 @@ func toEtcdOperationResult(resp *etcdserverpb.ResponseOp) EtcdOperationResult {
ValueRevision: ValueRevision{
Value: ToValueOrHash(string(kv.Value)),
ModRevision: kv.ModRevision,
Version: kv.Version,
},
}
}
@@ -342,7 +347,11 @@ func emptyGetResponse(revision int64) MaybeEtcdResponse {
}

func getResponse(key, value string, modRevision, revision int64) MaybeEtcdResponse {
return rangeResponse([]*mvccpb.KeyValue{{Key: []byte(key), Value: []byte(value), ModRevision: modRevision}}, 1, revision)
return getResponseWithVer(key, value, modRevision, 1, revision)
}

func getResponseWithVer(key, value string, modRevision, ver, revision int64) MaybeEtcdResponse {
return rangeResponse([]*mvccpb.KeyValue{{Key: []byte(key), Value: []byte(value), ModRevision: modRevision, Version: ver}}, 1, revision)
}

func rangeResponse(kvs []*mvccpb.KeyValue, count int64, revision int64) MaybeEtcdResponse {
@@ -354,6 +363,7 @@ func rangeResponse(kvs []*mvccpb.KeyValue, count int64, revision int64) MaybeEtc
ValueRevision: ValueRevision{
Value: ToValueOrHash(string(kv.Value)),
ModRevision: kv.ModRevision,
Version: kv.Version,
},
}
}
22 changes: 11 additions & 11 deletions tests/robustness/model/non_deterministic_test.go
Original file line number Diff line number Diff line change
@@ -33,15 +33,15 @@ func TestModelNonDeterministic(t *testing.T) {
operations: []testOperation{
{req: putRequest("key1", "1"), resp: failedResponse(errors.New("failed"))},
{req: putRequest("key2", "2"), resp: putResponse(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, Version: 1}, {Key: []byte("key2"), Value: []byte("2"), ModRevision: 3, Version: 1}}, 2, 3)},
},
},
{
name: "First Put request fails, and is lost",
operations: []testOperation{
{req: putRequest("key1", "1"), resp: failedResponse(errors.New("failed"))},
{req: putRequest("key2", "2"), resp: putResponse(2)},
{req: listRequest("key", 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, Version: 1}}, 1, 2)},
},
},
{
@@ -90,14 +90,14 @@ func TestModelNonDeterministic(t *testing.T) {
// One failed request, one persisted.
{req: putRequest("key", "1"), resp: putResponse(2)},
{req: putRequest("key", "2"), resp: failedResponse(errors.New("failed"))},
{req: getRequest("key"), resp: getResponse("key", "3", 3, 3), expectFailure: true},
{req: getRequest("key"), resp: getResponse("key", "3", 2, 3), expectFailure: true},
{req: getRequest("key"), resp: getResponse("key", "2", 2, 2), expectFailure: true},
{req: getRequest("key"), resp: getResponse("key", "2", 3, 3)},
{req: getRequest("key"), resp: getResponseWithVer("key", "3", 3, 2, 3), expectFailure: true},
{req: getRequest("key"), resp: getResponseWithVer("key", "3", 2, 2, 3), expectFailure: true},
{req: getRequest("key"), resp: getResponseWithVer("key", "2", 2, 2, 2), expectFailure: true},
{req: getRequest("key"), resp: getResponseWithVer("key", "2", 3, 2, 3)},
// Two failed request, two persisted.
{req: putRequest("key", "3"), resp: failedResponse(errors.New("failed"))},
{req: putRequest("key", "4"), resp: failedResponse(errors.New("failed"))},
{req: getRequest("key"), resp: getResponse("key", "4", 5, 5)},
{req: getRequest("key"), resp: getResponseWithVer("key", "4", 5, 4, 5)},
},
},
{
@@ -133,7 +133,7 @@ func TestModelNonDeterministic(t *testing.T) {
{req: putRequest("key", "4"), resp: putResponse(4)},
{req: compareRevisionAndPutRequest("key", 5, ""), resp: compareRevisionAndPutResponse(false, 4)},
{req: putRequest("key", "5"), resp: failedResponse(errors.New("failed"))},
{req: getRequest("key"), resp: getResponse("key", "5", 5, 5)},
{req: getRequest("key"), resp: getResponseWithVer("key", "5", 5, 4, 5)},
},
},
{
@@ -249,13 +249,13 @@ func TestModelNonDeterministic(t *testing.T) {
// One failed request, one persisted.
{req: putRequest("key", "1"), resp: putResponse(2)},
{req: compareRevisionAndPutRequest("key", 2, "2"), resp: failedResponse(errors.New("failed"))},
{req: getRequest("key"), resp: getResponse("key", "2", 2, 2), expectFailure: true},
{req: getRequest("key"), resp: getResponse("key", "2", 3, 3)},
{req: getRequest("key"), resp: getResponseWithVer("key", "2", 2, 2, 2), expectFailure: true},
{req: getRequest("key"), resp: getResponseWithVer("key", "2", 3, 2, 3)},
// Two failed request, two persisted.
{req: putRequest("key", "3"), resp: putResponse(4)},
{req: compareRevisionAndPutRequest("key", 4, "4"), resp: failedResponse(errors.New("failed"))},
{req: compareRevisionAndPutRequest("key", 5, "5"), resp: failedResponse(errors.New("failed"))},
{req: getRequest("key"), resp: getResponse("key", "5", 6, 6)},
{req: getRequest("key"), resp: getResponseWithVer("key", "5", 6, 5, 6)},
},
},
{
7 changes: 7 additions & 0 deletions tests/robustness/report/wal.go
Original file line number Diff line number Diff line change
@@ -199,6 +199,13 @@ func parseEntryNormal(ent raftpb.Entry) (*model.EtcdRequest, error) {
OperationsOnFailure: []model.EtcdOperation{},
}
for _, cmp := range raftReq.Txn.Compare {
if ver := cmp.GetVersion(); ver > 0 {
txn.Conditions = append(txn.Conditions, model.EtcdCondition{
Key: string(cmp.Key),
ExpectedVersion: ver,
})
continue
}
txn.Conditions = append(txn.Conditions, model.EtcdCondition{
Key: string(cmp.Key),
ExpectedRevision: cmp.GetModRevision(),
6 changes: 2 additions & 4 deletions tests/robustness/traffic/kubernetes.go
Original file line number Diff line number Diff line change
@@ -235,9 +235,8 @@ const (

func compact(ctx context.Context, client *client.RecordingClient, t, rev int64) (int64, int64, error) {
// Based on https://github.com/kubernetes/apiserver/blob/7dd4904f1896e11244ba3c5a59797697709de6b6/pkg/storage/etcd3/compact.go#L133-L162
// TODO: Use Version and not ModRevision when model supports key versioning.
resp, err := client.Txn(ctx).
If(clientv3.Compare(clientv3.ModRevision(compactRevKey), "=", t)).
If(clientv3.Compare(clientv3.Version(compactRevKey), "=", t)).
Then(clientv3.OpPut(compactRevKey, strconv.FormatInt(rev, 10))).
Else(clientv3.OpGet(compactRevKey)).
Commit()
@@ -248,8 +247,7 @@ func compact(ctx context.Context, client *client.RecordingClient, t, rev int64)
curRev := resp.Header.Revision

if !resp.Succeeded {
// TODO: Use Version and not ModRevision when model supports key versioning.
curTime := resp.Responses[0].GetResponseRange().Kvs[0].ModRevision
curTime := resp.Responses[0].GetResponseRange().Kvs[0].Version
return curTime, curRev, nil
}
curTime := t + 1
1 change: 1 addition & 0 deletions tests/robustness/validate/operations_test.go
Original file line number Diff line number Diff line change
@@ -290,6 +290,7 @@ func keyValueRevision(key, value string, rev int64) model.KeyValue {
ValueRevision: model.ValueRevision{
Value: model.ToValueOrHash(value),
ModRevision: rev,
Version: 1,
},
}
}
14 changes: 12 additions & 2 deletions tests/robustness/validate/validate_test.go
Original file line number Diff line number Diff line change
@@ -1633,8 +1633,8 @@ func TestValidateWatch(t *testing.T) {
{
Events: []model.WatchEvent{
putWatchEvent("a", "1", 2, true),
putWatchEventWithPrevKV("a", "2", 3, false, "1", 2),
deleteWatchEventWithPrevKV("a", 4, "2", 3),
putWatchEventWithPrevKVV("a", "2", 3, false, "1", 2, 1),
deleteWatchEventWithPrevKVV("a", 4, "2", 3, 2),
putWatchEvent("a", "4", 5, true),
},
},
@@ -1864,21 +1864,31 @@ func deleteWatchEvent(key string, rev int64) model.WatchEvent {
}

func putWatchEventWithPrevKV(key, value string, rev int64, isCreate bool, prevValue string, modRev int64) model.WatchEvent {
return putWatchEventWithPrevKVV(key, value, rev, isCreate, prevValue, modRev, 0)
}

func putWatchEventWithPrevKVV(key, value string, rev int64, isCreate bool, prevValue string, modRev, ver int64) model.WatchEvent {
return model.WatchEvent{
PersistedEvent: putPersistedEvent(key, value, rev, isCreate),
PrevValue: &model.ValueRevision{
Value: model.ToValueOrHash(prevValue),
ModRevision: modRev,
Version: ver,
},
}
}

func deleteWatchEventWithPrevKV(key string, rev int64, prevValue string, modRev int64) model.WatchEvent {
return deleteWatchEventWithPrevKVV(key, rev, prevValue, modRev, 0)
}

func deleteWatchEventWithPrevKVV(key string, rev int64, prevValue string, modRev, ver int64) model.WatchEvent {
return model.WatchEvent{
PersistedEvent: deletePersistedEvent(key, rev),
PrevValue: &model.ValueRevision{
Value: model.ToValueOrHash(prevValue),
ModRevision: modRev,
Version: ver,
},
}
}

0 comments on commit 4fcd7d0

Please sign in to comment.