Skip to content

Commit 8ee6e98

Browse files
author
Chun-Hung Tseng
committed
Patch the return time with MaxInt64 in robustness test
Reference: - #19579 Signed-off-by: Chun-Hung Tseng <[email protected]>
1 parent 53b88df commit 8ee6e98

File tree

4 files changed

+64
-91
lines changed

4 files changed

+64
-91
lines changed

tests/robustness/model/history.go

+2-30
Original file line numberDiff line numberDiff line change
@@ -290,8 +290,6 @@ func (h *AppendableHistory) appendFailed(request EtcdRequest, start, end time.Du
290290
}
291291
isRead := request.IsRead()
292292
if !isRead {
293-
// Failed writes can still be persisted, setting -1 for now as don't know when request has took effect.
294-
op.Return = -1
295293
// Operations of single client needs to be sequential.
296294
// As we don't know return time of failed operations, all new writes need to be done with new stream id.
297295
h.streamID = h.idProvider.NewStreamID()
@@ -300,7 +298,7 @@ func (h *AppendableHistory) appendFailed(request EtcdRequest, start, end time.Du
300298
}
301299

302300
func (h *AppendableHistory) append(op porcupine.Operation) {
303-
if op.Return != -1 && op.Call >= op.Return {
301+
if op.Call >= op.Return {
304302
panic(fmt.Sprintf("Invalid operation, call(%d) >= return(%d)", op.Call, op.Return))
305303
}
306304
if len(h.operations) > 0 {
@@ -488,36 +486,10 @@ func (h History) Len() int {
488486

489487
func (h History) Operations() []porcupine.Operation {
490488
operations := make([]porcupine.Operation, 0, len(h.operations))
491-
maxTime := h.lastObservedTime()
492-
for _, op := range h.operations {
493-
// Failed requests don't have a known return time.
494-
if op.Return == -1 {
495-
// Simulate Infinity by using last observed time.
496-
op.Return = maxTime + time.Second.Nanoseconds()
497-
}
498-
operations = append(operations, op)
499-
}
489+
operations = append(operations, h.operations...)
500490
return operations
501491
}
502492

503-
func (h History) lastObservedTime() int64 {
504-
var maxTime int64
505-
for _, op := range h.operations {
506-
if op.Return == -1 {
507-
// Collect call time from failed operations
508-
if op.Call > maxTime {
509-
maxTime = op.Call
510-
}
511-
} else {
512-
// Collect return time from successful operations
513-
if op.Return > maxTime {
514-
maxTime = op.Return
515-
}
516-
}
517-
}
518-
return maxTime
519-
}
520-
521493
func (h History) MaxRevision() int64 {
522494
var maxRevision int64
523495
for _, op := range h.operations {

tests/robustness/validate/operations.go

+17
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ package validate
1717
import (
1818
"errors"
1919
"fmt"
20+
"math"
2021
"time"
2122

2223
"github.com/anishathalye/porcupine"
@@ -53,6 +54,8 @@ func validateLinearizableOperationsAndVisualize(
5354
operations []porcupine.Operation,
5455
timeout time.Duration,
5556
) (results Results) {
57+
operations = patchResponseTimeOfFailedOperations(operations)
58+
5659
lg.Info("Validating linearizable operations", zap.Duration("timeout", timeout))
5760
start := time.Now()
5861
result, info := porcupine.CheckOperationsVerbose(model.NonDeterministicModel, operations, timeout)
@@ -75,6 +78,20 @@ func validateLinearizableOperationsAndVisualize(
7578
}
7679
}
7780

81+
// Failed writes can still be persisted, but we don't know when request has took effect, so we override it with MaxInt64
82+
func patchResponseTimeOfFailedOperations(operations []porcupine.Operation) []porcupine.Operation {
83+
patchedOperations := make([]porcupine.Operation, 0, len(operations))
84+
for _, op := range operations {
85+
request := op.Input.(model.EtcdRequest)
86+
resp := op.Output.(model.MaybeEtcdResponse)
87+
if resp.Error != "" && !request.IsRead() {
88+
op.Return = math.MaxInt64
89+
patchedOperations = append(patchedOperations, op)
90+
}
91+
}
92+
return patchedOperations
93+
}
94+
7895
func validateSerializableOperations(lg *zap.Logger, operations []porcupine.Operation, replay *model.EtcdReplay) (lastErr error) {
7996
lg.Info("Validating serializable operations")
8097
for _, read := range operations {

tests/robustness/validate/patch_history.go

+21-37
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ func patchLinearizableOperations(reports []report.ClientReport, persistedRequest
2828
putRevision := putRevision(reports)
2929
persistedPutCount := countPersistedPuts(persistedRequests)
3030
clientPutCount := countClientPuts(reports)
31-
putReturnTime := uniquePutReturnTime(allOperations, reports, persistedRequests, clientPutCount)
31+
putReturnTime := uniquePutReturnTime(allOperations, reports, clientPutCount)
3232
return patchOperations(allOperations, putRevision, putReturnTime, clientPutCount, persistedPutCount)
3333
}
3434

@@ -96,7 +96,7 @@ func patchOperations(operations []porcupine.Operation, watchRevision, putReturnT
9696
txnRevision = revision
9797
}
9898
if returnTime, ok := putReturnTime[kv]; ok {
99-
op.Return = min(op.Return, returnTime)
99+
op.Return = returnTime
100100
}
101101
case model.DeleteOperation:
102102
case model.RangeOperation:
@@ -110,9 +110,9 @@ func patchOperations(operations []porcupine.Operation, watchRevision, putReturnT
110110
continue
111111
}
112112
if txnRevision != 0 {
113-
op.Output = model.MaybeEtcdResponse{Persisted: true, PersistedRevision: txnRevision}
113+
op.Output = model.MaybeEtcdResponse{Persisted: true, PersistedRevision: txnRevision, Error: resp.Error}
114114
} else {
115-
op.Output = model.MaybeEtcdResponse{Persisted: true}
115+
op.Output = model.MaybeEtcdResponse{Persisted: true, Error: resp.Error}
116116
}
117117
}
118118
// Leave operation as it is as we cannot discard it.
@@ -155,11 +155,13 @@ func hasUniqueWriteOperation(ops []model.EtcdOperation, clientRequestCount map[k
155155
return false
156156
}
157157

158-
func uniquePutReturnTime(allOperations []porcupine.Operation, reports []report.ClientReport, persistedRequests []model.EtcdRequest, clientPutCount map[keyValue]int64) map[keyValue]int64 {
158+
func uniquePutReturnTime(allOperations []porcupine.Operation, reports []report.ClientReport, clientPutCount map[keyValue]int64) map[keyValue]int64 {
159159
earliestReturnTime := map[keyValue]int64{}
160-
var lastReturnTime int64
160+
failedRequest := map[keyValue]bool{}
161+
161162
for _, op := range allOperations {
162163
request := op.Input.(model.EtcdRequest)
164+
resp := op.Output.(model.MaybeEtcdResponse)
163165
switch request.Type {
164166
case model.Txn:
165167
for _, etcdOp := range append(request.Txn.OperationsOnSuccess, request.Txn.OperationsOnFailure...) {
@@ -170,10 +172,14 @@ func uniquePutReturnTime(allOperations []porcupine.Operation, reports []report.C
170172
if count := clientPutCount[kv]; count > 1 {
171173
continue
172174
}
175+
if resp.Error != "" {
176+
failedRequest[kv] = true
177+
}
173178
if returnTime, ok := earliestReturnTime[kv]; !ok || returnTime > op.Return {
179+
// The time that the response is received
180+
// This doesn't mean anything for the failed request, as the request might have actually succeeded (e.g. connection dropped, etc.)
174181
earliestReturnTime[kv] = op.Return
175182
}
176-
earliestReturnTime[kv] = op.Return
177183
}
178184
case model.Range:
179185
case model.LeaseGrant:
@@ -182,11 +188,11 @@ func uniquePutReturnTime(allOperations []porcupine.Operation, reports []report.C
182188
default:
183189
panic(fmt.Sprintf("Unknown request type: %q", request.Type))
184190
}
185-
if op.Return > lastReturnTime {
186-
lastReturnTime = op.Return
187-
}
188191
}
189192

193+
// for failed requests, we use the time that the watch is received as the return time
194+
// since the values returned by the watch are the values that are actually written in the database
195+
// notice that the time that the value is sent by the watch might be earlier or later than the response received time due to issues such as network problems
190196
for _, client := range reports {
191197
for _, watch := range client.Watch {
192198
for _, resp := range watch.Responses {
@@ -198,8 +204,12 @@ func uniquePutReturnTime(allOperations []porcupine.Operation, reports []report.C
198204
if count := clientPutCount[kv]; count > 1 {
199205
continue
200206
}
201-
if t, ok := earliestReturnTime[kv]; !ok || t > resp.Time.Nanoseconds() {
207+
if _, ok := failedRequest[kv]; ok {
202208
earliestReturnTime[kv] = resp.Time.Nanoseconds()
209+
} else {
210+
if t, ok := earliestReturnTime[kv]; !ok || t > resp.Time.Nanoseconds() {
211+
earliestReturnTime[kv] = resp.Time.Nanoseconds()
212+
}
203213
}
204214
case model.DeleteOperation:
205215
default:
@@ -210,32 +220,6 @@ func uniquePutReturnTime(allOperations []porcupine.Operation, reports []report.C
210220
}
211221
}
212222

213-
for i := len(persistedRequests) - 1; i >= 0; i-- {
214-
request := persistedRequests[i]
215-
switch request.Type {
216-
case model.Txn:
217-
lastReturnTime--
218-
for _, op := range request.Txn.OperationsOnSuccess {
219-
if op.Type != model.PutOperation {
220-
continue
221-
}
222-
kv := keyValue{Key: op.Put.Key, Value: op.Put.Value}
223-
if count := clientPutCount[kv]; count > 1 {
224-
continue
225-
}
226-
returnTime, ok := earliestReturnTime[kv]
227-
if ok {
228-
lastReturnTime = min(returnTime, lastReturnTime)
229-
earliestReturnTime[kv] = lastReturnTime
230-
}
231-
}
232-
case model.LeaseGrant:
233-
case model.LeaseRevoke:
234-
case model.Compact:
235-
default:
236-
panic(fmt.Sprintf("Unknown request type: %q", request.Type))
237-
}
238-
}
239223
return earliestReturnTime
240224
}
241225

0 commit comments

Comments
 (0)