Skip to content

Commit 2f253e5

Browse files
committed
tests: Patch unique non-put ops in robustness
Signed-off-by: Ashwani Kumar Kamal <[email protected]>
1 parent 1b2ed5a commit 2f253e5

File tree

2 files changed

+192
-51
lines changed

2 files changed

+192
-51
lines changed

tests/robustness/validate/patch_history.go

Lines changed: 185 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -24,24 +24,55 @@ import (
2424
"go.etcd.io/etcd/tests/v3/robustness/report"
2525
)
2626

27+
type patchArgs struct {
28+
returnTime int64
29+
clientCount int64
30+
persistedCount int64
31+
}
32+
2733
func patchLinearizableOperations(operations []porcupine.Operation, reports []report.ClientReport, persistedRequests []model.EtcdRequest) []porcupine.Operation {
2834
putRevision := putRevision(reports)
35+
2936
persistedPutCount := countPersistedPuts(persistedRequests)
3037
clientPutCount := countClientPuts(reports)
31-
putReturnTime := uniquePutReturnTime(operations, persistedRequests, clientPutCount)
32-
return patchOperations(operations, putRevision, putReturnTime, clientPutCount, persistedPutCount)
38+
39+
persistedDeleteCount := countPersistedDeletes(persistedRequests)
40+
clientDeleteCount := countClientDeletes(reports)
41+
42+
putReturnTime, delReturnTime := uniqueOperationReturnTime(operations, persistedRequests, clientPutCount, clientDeleteCount)
43+
44+
putArgs := make(map[model.PutOptions]patchArgs)
45+
for opts, c := range clientPutCount {
46+
putArgs[opts] = patchArgs{
47+
clientCount: c,
48+
persistedCount: persistedPutCount[opts],
49+
returnTime: putReturnTime[opts],
50+
}
51+
}
52+
delArgs := make(map[model.DeleteOptions]patchArgs)
53+
for opts, c := range clientDeleteCount {
54+
delArgs[opts] = patchArgs{
55+
clientCount: c,
56+
persistedCount: persistedDeleteCount[opts],
57+
returnTime: delReturnTime[opts],
58+
}
59+
}
60+
61+
return patchOperations(
62+
operations, putRevision, putArgs, delArgs,
63+
)
3364
}
3465

35-
func putRevision(reports []report.ClientReport) map[keyValue]int64 {
36-
requestRevision := map[keyValue]int64{}
66+
func putRevision(reports []report.ClientReport) map[model.PutOptions]int64 {
67+
requestRevision := map[model.PutOptions]int64{}
3768
for _, client := range reports {
3869
for _, watch := range client.Watch {
3970
for _, resp := range watch.Responses {
4071
for _, event := range resp.Events {
4172
switch event.Type {
4273
case model.RangeOperation:
4374
case model.PutOperation:
44-
kv := keyValue{Key: event.Key, Value: event.Value}
75+
kv := model.PutOptions{Key: event.Key, Value: event.Value}
4576
requestRevision[kv] = event.Revision
4677
case model.DeleteOperation:
4778
default:
@@ -54,7 +85,12 @@ func putRevision(reports []report.ClientReport) map[keyValue]int64 {
5485
return requestRevision
5586
}
5687

57-
func patchOperations(operations []porcupine.Operation, watchRevision, putReturnTime, clientPutCount, persistedPutCount map[keyValue]int64) []porcupine.Operation {
88+
func patchOperations(
89+
operations []porcupine.Operation,
90+
putRevision map[model.PutOptions]int64,
91+
putArgs map[model.PutOptions]patchArgs,
92+
delArgs map[model.DeleteOptions]patchArgs,
93+
) []porcupine.Operation {
5894
newOperations := make([]porcupine.Operation, 0, len(operations))
5995

6096
for _, op := range operations {
@@ -70,26 +106,44 @@ func patchOperations(operations []porcupine.Operation, watchRevision, putReturnT
70106
for _, etcdOp := range append(request.Txn.OperationsOnSuccess, request.Txn.OperationsOnFailure...) {
71107
switch etcdOp.Type {
72108
case model.PutOperation:
73-
kv := keyValue{Key: etcdOp.Put.Key, Value: etcdOp.Put.Value}
74-
if _, ok := persistedPutCount[kv]; ok {
109+
kv := model.PutOptions{Key: etcdOp.Put.Key, Value: etcdOp.Put.Value}
110+
arg, ok := putArgs[kv]
111+
if !ok {
112+
continue
113+
}
114+
if arg.persistedCount > 0 {
75115
persisted = true
76116
}
77-
if count := clientPutCount[kv]; count != 1 {
117+
if arg.clientCount != 1 {
78118
continue
79119
}
80-
if revision, ok := watchRevision[kv]; ok {
120+
if revision, ok := putRevision[kv]; ok {
81121
txnRevision = revision
82122
}
83-
if returnTime, ok := putReturnTime[kv]; ok {
84-
op.Return = min(op.Return, returnTime)
123+
if arg.returnTime > 0 {
124+
op.Return = min(op.Return, arg.returnTime)
85125
}
86126
case model.DeleteOperation:
127+
key := model.DeleteOptions{Key: etcdOp.Put.Key}
128+
arg, ok := delArgs[key]
129+
if !ok {
130+
continue
131+
}
132+
if arg.persistedCount > 0 {
133+
persisted = true
134+
}
135+
if arg.clientCount != 1 {
136+
continue
137+
}
138+
if arg.returnTime > 0 {
139+
op.Return = min(op.Return, arg.returnTime)
140+
}
87141
case model.RangeOperation:
88142
default:
89143
panic(fmt.Sprintf("unknown operation type %q", etcdOp.Type))
90144
}
91145
}
92-
if isUniqueTxn(request.Txn, clientPutCount) {
146+
if isUniqueTxn(request.Txn, putArgs, delArgs) {
93147
if !persisted {
94148
// Remove non persisted operations
95149
continue
@@ -106,12 +160,21 @@ func patchOperations(operations []porcupine.Operation, watchRevision, putReturnT
106160
return newOperations
107161
}
108162

109-
func isUniqueTxn(request *model.TxnRequest, clientRequestCount map[keyValue]int64) bool {
110-
return isUniqueOps(request.OperationsOnSuccess, clientRequestCount) && isUniqueOps(request.OperationsOnFailure, clientRequestCount)
163+
func containsDeleteOp(txn *model.TxnRequest) bool {
164+
for _, op := range append(txn.OperationsOnSuccess, txn.OperationsOnFailure...) {
165+
if op.Type == model.DeleteOperation {
166+
return true
167+
}
168+
}
169+
return false
170+
}
171+
172+
func isUniqueTxn(request *model.TxnRequest, putArgs map[model.PutOptions]patchArgs, delArgs map[model.DeleteOptions]patchArgs) bool {
173+
return isUniqueOps(request.OperationsOnSuccess, putArgs, delArgs) && isUniqueOps(request.OperationsOnFailure, putArgs, delArgs)
111174
}
112175

113-
func isUniqueOps(ops []model.EtcdOperation, clientRequestCount map[keyValue]int64) bool {
114-
return hasUniqueWriteOperation(ops, clientRequestCount) || !hasWriteOperation(ops)
176+
func isUniqueOps(ops []model.EtcdOperation, putArgs map[model.PutOptions]patchArgs, delArgs map[model.DeleteOptions]patchArgs) bool {
177+
return hasUniqueWriteOperation(ops, putArgs, delArgs) || !hasWriteOperation(ops)
115178
}
116179

117180
func hasWriteOperation(ops []model.EtcdOperation) bool {
@@ -123,15 +186,19 @@ func hasWriteOperation(ops []model.EtcdOperation) bool {
123186
return false
124187
}
125188

126-
func hasUniqueWriteOperation(ops []model.EtcdOperation, clientRequestCount map[keyValue]int64) bool {
189+
func hasUniqueWriteOperation(ops []model.EtcdOperation, putArgs map[model.PutOptions]patchArgs, delArgs map[model.DeleteOptions]patchArgs) bool {
127190
for _, operation := range ops {
128191
switch operation.Type {
129192
case model.PutOperation:
130-
kv := keyValue{Key: operation.Put.Key, Value: operation.Put.Value}
131-
if count := clientRequestCount[kv]; count == 1 {
193+
opt := model.PutOptions{Key: operation.Put.Key, Value: operation.Put.Value}
194+
if arg, ok := putArgs[opt]; ok && arg.clientCount == 1 {
132195
return true
133196
}
134197
case model.DeleteOperation:
198+
opt := model.DeleteOptions{Key: operation.Delete.Key}
199+
if arg, ok := delArgs[opt]; ok && arg.clientCount == 1 {
200+
return true
201+
}
135202
case model.RangeOperation:
136203
default:
137204
panic(fmt.Sprintf("unknown operation type %q", operation.Type))
@@ -140,25 +207,33 @@ func hasUniqueWriteOperation(ops []model.EtcdOperation, clientRequestCount map[k
140207
return false
141208
}
142209

143-
func uniquePutReturnTime(allOperations []porcupine.Operation, persistedRequests []model.EtcdRequest, clientPutCount map[keyValue]int64) map[keyValue]int64 {
144-
earliestReturnTime := map[keyValue]int64{}
210+
func uniqueOperationReturnTime(allOps []porcupine.Operation, persistedRequests []model.EtcdRequest, clientPutCount map[model.PutOptions]int64, clientDeleteCount map[model.DeleteOptions]int64) (map[model.PutOptions]int64, map[model.DeleteOptions]int64) {
211+
putTimes := map[model.PutOptions]int64{}
212+
delTimes := map[model.DeleteOptions]int64{}
145213
var lastReturnTime int64
146-
for _, op := range allOperations {
214+
for _, op := range allOps {
147215
request := op.Input.(model.EtcdRequest)
148216
switch request.Type {
149217
case model.Txn:
150218
for _, etcdOp := range append(request.Txn.OperationsOnSuccess, request.Txn.OperationsOnFailure...) {
151-
if etcdOp.Type != model.PutOperation {
152-
continue
153-
}
154-
kv := keyValue{Key: etcdOp.Put.Key, Value: etcdOp.Put.Value}
155-
if count := clientPutCount[kv]; count > 1 {
156-
continue
157-
}
158-
if returnTime, ok := earliestReturnTime[kv]; !ok || returnTime > op.Return {
159-
earliestReturnTime[kv] = op.Return
219+
switch etcdOp.Type {
220+
case model.PutOperation:
221+
opt := model.PutOptions{Key: etcdOp.Put.Key, Value: etcdOp.Put.Value}
222+
if clientPutCount[opt] > 1 {
223+
continue
224+
}
225+
if t, ok := putTimes[opt]; !ok || op.Return < t {
226+
putTimes[opt] = op.Return
227+
}
228+
case model.DeleteOperation:
229+
opt := model.DeleteOptions{Key: etcdOp.Delete.Key}
230+
if clientDeleteCount[opt] > 1 {
231+
continue
232+
}
233+
if t, ok := delTimes[opt]; !ok || op.Return < t {
234+
delTimes[opt] = op.Return
235+
}
160236
}
161-
earliestReturnTime[kv] = op.Return
162237
}
163238
case model.Range:
164239
case model.LeaseGrant:
@@ -181,17 +256,25 @@ func uniquePutReturnTime(allOperations []porcupine.Operation, persistedRequests
181256
lastReturnTime--
182257
}
183258
for _, op := range request.Txn.OperationsOnSuccess {
184-
if op.Type != model.PutOperation {
185-
continue
186-
}
187-
kv := keyValue{Key: op.Put.Key, Value: op.Put.Value}
188-
if count := clientPutCount[kv]; count > 1 {
189-
continue
190-
}
191-
returnTime, ok := earliestReturnTime[kv]
192-
if ok {
193-
lastReturnTime = min(returnTime, lastReturnTime)
194-
earliestReturnTime[kv] = lastReturnTime
259+
switch op.Type {
260+
case model.PutOperation:
261+
opt := model.PutOptions{Key: op.Put.Key, Value: op.Put.Value}
262+
if clientPutCount[opt] > 1 {
263+
continue
264+
}
265+
if returnTime, ok := putTimes[opt]; ok {
266+
lastReturnTime = min(returnTime, lastReturnTime)
267+
putTimes[opt] = lastReturnTime
268+
}
269+
case model.DeleteOperation:
270+
opt := model.DeleteOptions{Key: op.Delete.Key}
271+
if clientDeleteCount[opt] > 1 {
272+
continue
273+
}
274+
if returnTime, ok := delTimes[opt]; ok {
275+
lastReturnTime = min(returnTime, lastReturnTime)
276+
delTimes[opt] = lastReturnTime
277+
}
195278
}
196279
}
197280
case model.LeaseGrant:
@@ -201,11 +284,11 @@ func uniquePutReturnTime(allOperations []porcupine.Operation, persistedRequests
201284
panic(fmt.Sprintf("Unknown request type: %q", request.Type))
202285
}
203286
}
204-
return earliestReturnTime
287+
return putTimes, delTimes
205288
}
206289

207-
func countClientPuts(reports []report.ClientReport) map[keyValue]int64 {
208-
counter := map[keyValue]int64{}
290+
func countClientPuts(reports []report.ClientReport) map[model.PutOptions]int64 {
291+
counter := map[model.PutOptions]int64{}
209292
for _, client := range reports {
210293
for _, op := range client.KeyValue {
211294
request := op.Input.(model.EtcdRequest)
@@ -215,21 +298,21 @@ func countClientPuts(reports []report.ClientReport) map[keyValue]int64 {
215298
return counter
216299
}
217300

218-
func countPersistedPuts(requests []model.EtcdRequest) map[keyValue]int64 {
219-
counter := map[keyValue]int64{}
301+
func countPersistedPuts(requests []model.EtcdRequest) map[model.PutOptions]int64 {
302+
counter := map[model.PutOptions]int64{}
220303
for _, request := range requests {
221304
countPuts(counter, request)
222305
}
223306
return counter
224307
}
225308

226-
func countPuts(counter map[keyValue]int64, request model.EtcdRequest) {
309+
func countPuts(counter map[model.PutOptions]int64, request model.EtcdRequest) {
227310
switch request.Type {
228311
case model.Txn:
229312
for _, operation := range append(request.Txn.OperationsOnSuccess, request.Txn.OperationsOnFailure...) {
230313
switch operation.Type {
231314
case model.PutOperation:
232-
kv := keyValue{Key: operation.Put.Key, Value: operation.Put.Value}
315+
kv := model.PutOptions{Key: operation.Put.Key, Value: operation.Put.Value}
233316
counter[kv]++
234317
case model.DeleteOperation:
235318
case model.RangeOperation:
@@ -247,6 +330,57 @@ func countPuts(counter map[keyValue]int64, request model.EtcdRequest) {
247330
}
248331
}
249332

333+
func uniqueDeleteReturnTime(allOps []porcupine.Operation, persisted []model.EtcdRequest, clientDeleteCount map[string]int64) map[string]int64 {
334+
times := map[string]int64{}
335+
for _, op := range allOps {
336+
req := op.Input.(model.EtcdRequest)
337+
if req.Type == model.Txn {
338+
for _, e := range append(req.Txn.OperationsOnSuccess, req.Txn.OperationsOnFailure...) {
339+
if e.Type != model.DeleteOperation {
340+
continue
341+
}
342+
k := e.Delete.Key
343+
if clientDeleteCount[k] == 1 {
344+
if t, ok := times[k]; !ok || op.Return < t {
345+
times[k] = op.Return
346+
}
347+
}
348+
}
349+
}
350+
}
351+
return times
352+
}
353+
354+
func countClientDeletes(reports []report.ClientReport) map[model.DeleteOptions]int64 {
355+
counter := map[model.DeleteOptions]int64{}
356+
for _, client := range reports {
357+
for _, op := range client.KeyValue {
358+
request := op.Input.(model.EtcdRequest)
359+
countDeletes(counter, request)
360+
}
361+
}
362+
return counter
363+
}
364+
365+
func countPersistedDeletes(requests []model.EtcdRequest) map[model.DeleteOptions]int64 {
366+
counter := map[model.DeleteOptions]int64{}
367+
for _, req := range requests {
368+
countDeletes(counter, req)
369+
}
370+
return counter
371+
}
372+
373+
func countDeletes(counter map[model.DeleteOptions]int64, request model.EtcdRequest) {
374+
if request.Type != model.Txn {
375+
return
376+
}
377+
for _, operation := range append(request.Txn.OperationsOnSuccess, request.Txn.OperationsOnFailure...) {
378+
if operation.Type == model.DeleteOperation {
379+
counter[operation.Delete]++
380+
}
381+
}
382+
}
383+
250384
type keyValue struct {
251385
Key string
252386
Value model.ValueOrHash

tests/robustness/validate/patch_history_test.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,3 +465,10 @@ func deleteEvent(key string, revision int64) model.WatchEvent {
465465
},
466466
}
467467
}
468+
469+
func compactRequest(rev int64) model.EtcdRequest {
470+
return model.EtcdRequest{
471+
Type: model.Compact,
472+
Compact: &model.CompactRequest{Revision: rev},
473+
}
474+
}

0 commit comments

Comments
 (0)