@@ -23,6 +23,8 @@ import (
23
23
"sort"
24
24
25
25
"github.com/anishathalye/porcupine"
26
+
27
+ "go.etcd.io/etcd/server/v3/storage/mvcc"
26
28
)
27
29
28
30
// DeterministicModel assumes a deterministic execution of etcd requests. All
@@ -64,10 +66,11 @@ var DeterministicModel = porcupine.Model{
64
66
}
65
67
66
68
type EtcdState struct {
67
- Revision int64
68
- KeyValues map [string ]ValueRevision
69
- KeyLeases map [string ]int64
70
- Leases map [int64 ]EtcdLease
69
+ Revision int64
70
+ CompactRevision int64
71
+ KeyValues map [string ]ValueRevision
72
+ KeyLeases map [string ]int64
73
+ Leases map [int64 ]EtcdLease
71
74
}
72
75
73
76
func (s EtcdState ) apply (request EtcdRequest , response EtcdResponse ) (bool , EtcdState ) {
@@ -77,10 +80,12 @@ func (s EtcdState) apply(request EtcdRequest, response EtcdResponse) (bool, Etcd
77
80
78
81
func freshEtcdState () EtcdState {
79
82
return EtcdState {
80
- Revision : 1 ,
81
- KeyValues : map [string ]ValueRevision {},
82
- KeyLeases : map [string ]int64 {},
83
- Leases : map [int64 ]EtcdLease {},
83
+ Revision : 1 ,
84
+ // Start from CompactRevision equal -1 as etcd allows client to compact revision 0 for some reason.
85
+ CompactRevision : - 1 ,
86
+ KeyValues : map [string ]ValueRevision {},
87
+ KeyLeases : map [string ]int64 {},
88
+ Leases : map [int64 ]EtcdLease {},
84
89
}
85
90
}
86
91
@@ -100,6 +105,9 @@ func (s EtcdState) Step(request EtcdRequest) (EtcdState, MaybeEtcdResponse) {
100
105
if request .Range .Revision > s .Revision {
101
106
return s , MaybeEtcdResponse {Error : ErrEtcdFutureRev .Error ()}
102
107
}
108
+ if request .Range .Revision < s .CompactRevision {
109
+ return s , MaybeEtcdResponse {EtcdResponse : EtcdResponse {ClientError : mvcc .ErrCompacted .Error ()}}
110
+ }
103
111
return s , MaybeEtcdResponse {PartialResponse : true , EtcdResponse : EtcdResponse {Revision : s .Revision }}
104
112
case Txn :
105
113
failure := false
@@ -178,6 +186,14 @@ func (s EtcdState) Step(request EtcdRequest) (EtcdState, MaybeEtcdResponse) {
178
186
return s , MaybeEtcdResponse {EtcdResponse : EtcdResponse {Revision : s .Revision , LeaseRevoke : & LeaseRevokeResponse {}}}
179
187
case Defragment :
180
188
return s , MaybeEtcdResponse {EtcdResponse : EtcdResponse {Defragment : & DefragmentResponse {}, Revision : s .Revision }}
189
+ case Compact :
190
+ if request .Compact .Revision <= s .CompactRevision {
191
+ return s , MaybeEtcdResponse {EtcdResponse : EtcdResponse {ClientError : mvcc .ErrCompacted .Error ()}}
192
+ }
193
+ s .CompactRevision = request .Compact .Revision
194
+ // Set fake revision as compaction returns non-linearizable revision.
195
+ // TODO: Model non-linearizable response revision in model.
196
+ return s , MaybeEtcdResponse {EtcdResponse : EtcdResponse {Compact : & CompactResponse {}, Revision : - 1 }}
181
197
default :
182
198
panic (fmt .Sprintf ("Unknown request type: %v" , request .Type ))
183
199
}
@@ -237,6 +253,7 @@ const (
237
253
LeaseGrant RequestType = "leaseGrant"
238
254
LeaseRevoke RequestType = "leaseRevoke"
239
255
Defragment RequestType = "defragment"
256
+ Compact RequestType = "compact"
240
257
)
241
258
242
259
type EtcdRequest struct {
@@ -246,6 +263,7 @@ type EtcdRequest struct {
246
263
Range * RangeRequest
247
264
Txn * TxnRequest
248
265
Defragment * DefragmentRequest
266
+ Compact * CompactRequest
249
267
}
250
268
251
269
func (r * EtcdRequest ) IsRead () bool {
@@ -337,6 +355,8 @@ type EtcdResponse struct {
337
355
LeaseGrant * LeaseGrantReponse
338
356
LeaseRevoke * LeaseRevokeResponse
339
357
Defragment * DefragmentResponse
358
+ Compact * CompactResponse
359
+ ClientError string
340
360
Revision int64
341
361
}
342
362
@@ -398,3 +418,10 @@ func ToValueOrHash(value string) ValueOrHash {
398
418
}
399
419
return v
400
420
}
421
+
422
+ type CompactResponse struct {
423
+ }
424
+
425
+ type CompactRequest struct {
426
+ Revision int64
427
+ }
0 commit comments