Skip to content

Commit

Permalink
Merge pull request #15178 from serathius/linearizability-defrag
Browse files Browse the repository at this point in the history
tests: Add defragment request to linearizability tests
  • Loading branch information
serathius authored Jan 24, 2023
2 parents 63902fa + c50e602 commit 91ec368
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 13 deletions.
8 changes: 8 additions & 0 deletions tests/linearizability/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,3 +125,11 @@ func (c *recordingClient) PutWithLease(ctx context.Context, key string, value st
c.history.AppendPutWithLease(key, value, int64(leaseId), callTime, returnTime, resp, err)
return err
}

func (c *recordingClient) Defragment(ctx context.Context) error {
callTime := time.Now()
resp, err := c.client.Defragment(ctx, c.client.Endpoints()[0])
returnTime := time.Now()
c.history.AppendDefragment(callTime, returnTime, resp, err)
return err
}
22 changes: 13 additions & 9 deletions tests/linearizability/linearizability_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ const (
)

var (
LowTrafficAllRequests = trafficConfig{
name: "LowTrafficAllRequests",
LowTraffic = trafficConfig{
name: "LowTraffic",
minimalQPS: 100,
maximalQPS: 200,
clientCount: 8,
Expand All @@ -52,19 +52,23 @@ var (
{operation: Delete, chance: 10},
{operation: PutWithLease, chance: 10},
{operation: LeaseRevoke, chance: 10},
{operation: CompareAndSet, chance: 20},
{operation: CompareAndSet, chance: 10},
{operation: Defragment, chance: 10},
}},
}
HighTrafficPut = trafficConfig{
name: "HighTrafficPut",
HighTraffic = trafficConfig{
name: "HighTraffic",
minimalQPS: 200,
maximalQPS: 1000,
clientCount: 12,
traffic: readWriteSingleKey{keyCount: 4, leaseTTL: DefaultLeaseTTL, writes: []requestChance{{operation: Put, chance: 100}}},
traffic: readWriteSingleKey{keyCount: 4, leaseTTL: DefaultLeaseTTL, writes: []requestChance{
{operation: Put, chance: 90},
{operation: Defragment, chance: 10},
}},
}
defaultTraffic = LowTrafficAllRequests
defaultTraffic = LowTraffic
trafficList = []trafficConfig{
LowTrafficAllRequests, HighTrafficPut,
LowTraffic, HighTraffic,
}
)

Expand Down Expand Up @@ -122,7 +126,7 @@ func TestLinearizability(t *testing.T) {
{
name: "Issue13766",
failpoint: KillFailpoint,
traffic: &HighTrafficPut,
traffic: &HighTraffic,
config: *e2e.NewConfig(
e2e.WithSnapshotCount(100),
),
Expand Down
23 changes: 23 additions & 0 deletions tests/linearizability/model/history.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,21 @@ func (h *AppendableHistory) AppendTxn(key, expectValue, newValue string, start,
})
}

func (h *AppendableHistory) AppendDefragment(start, end time.Time, resp *clientv3.DefragmentResponse, err error) {
request := defragmentRequest()
if err != nil {
h.appendFailed(request, start, err)
return
}
h.successful = append(h.successful, porcupine.Operation{
ClientId: h.id,
Input: request,
Call: start.UnixNano(),
Output: defragmentResponse(),
Return: end.UnixNano(),
})
}

func (h *AppendableHistory) appendFailed(request EtcdRequest, start time.Time, err error) {
h.failed = append(h.failed, porcupine.Operation{
ClientId: h.id,
Expand Down Expand Up @@ -257,6 +272,14 @@ func leaseRevokeResponse(revision int64) EtcdResponse {
return EtcdResponse{LeaseRevoke: &LeaseRevokeResponse{}, Revision: revision}
}

func defragmentRequest() EtcdRequest {
return EtcdRequest{Type: Defragment, Defragment: &DefragmentRequest{}}
}

func defragmentResponse() EtcdResponse {
return EtcdResponse{Defragment: &DefragmentResponse{}}
}

type History struct {
successful []porcupine.Operation
// failed requests are kept separate as we don't know return time of failed operations.
Expand Down
22 changes: 18 additions & 4 deletions tests/linearizability/model/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,15 @@ const (
Txn RequestType = "txn"
LeaseGrant RequestType = "leaseGrant"
LeaseRevoke RequestType = "leaseRevoke"
Defragment RequestType = "defragment"
)

type EtcdRequest struct {
Type RequestType
LeaseGrant *LeaseGrantRequest
LeaseRevoke *LeaseRevokeRequest
Txn *TxnRequest
Defragment *DefragmentRequest
}

type TxnRequest struct {
Expand All @@ -90,6 +92,7 @@ type LeaseGrantRequest struct {
type LeaseRevokeRequest struct {
LeaseID int64
}
type DefragmentRequest struct{}

type EtcdResponse struct {
Err error
Expand All @@ -98,6 +101,7 @@ type EtcdResponse struct {
Txn *TxnResponse
LeaseGrant *LeaseGrantReponse
LeaseRevoke *LeaseRevokeResponse
Defragment *DefragmentResponse
}

type TxnResponse struct {
Expand All @@ -109,6 +113,7 @@ type LeaseGrantReponse struct {
LeaseID int64
}
type LeaseRevokeResponse struct{}
type DefragmentResponse struct{}

func Match(r1, r2 EtcdResponse) bool {
return ((r1.ResultUnknown || r2.ResultUnknown) && (r1.Revision == r2.Revision)) || reflect.DeepEqual(r1, r2)
Expand Down Expand Up @@ -147,9 +152,11 @@ func describeEtcdResponse(request EtcdRequest, response EtcdResponse) string {
}
if request.Type == Txn {
return fmt.Sprintf("%s, rev: %d", describeTxnResponse(request.Txn, response.Txn), response.Revision)
} else {
return fmt.Sprintf("ok, rev: %d", response.Revision)
}
if response.Revision == 0 {
return "ok"
}
return fmt.Sprintf("ok, rev: %d", response.Revision)
}

func describeEtcdRequest(request EtcdRequest) string {
Expand All @@ -164,6 +171,8 @@ func describeEtcdRequest(request EtcdRequest) string {
return fmt.Sprintf("leaseGrant(%d)", request.LeaseGrant.LeaseID)
case LeaseRevoke:
return fmt.Sprintf("leaseRevoke(%d)", request.LeaseRevoke.LeaseID)
case Defragment:
return fmt.Sprintf("defragment()")
default:
return fmt.Sprintf("<! unknown request type: %q !>", request.Type)
}
Expand Down Expand Up @@ -231,7 +240,7 @@ func describeEtcdOperationResponse(op OperationType, resp EtcdOperationResult) s
func step(states PossibleStates, request EtcdRequest, response EtcdResponse) (bool, PossibleStates) {
if len(states) == 0 {
// states were not initialized
if response.Err != nil || response.ResultUnknown {
if response.Err != nil || response.ResultUnknown || response.Revision == 0 {
return true, nil
}
return true, PossibleStates{initState(request, response)}
Expand Down Expand Up @@ -278,6 +287,7 @@ func initState(request EtcdRequest, response EtcdResponse) EtcdState {
}
state.Leases[request.LeaseGrant.LeaseID] = lease
case LeaseRevoke:
case Defragment:
default:
panic(fmt.Sprintf("Unknown request type: %v", request.Type))
}
Expand All @@ -288,7 +298,9 @@ func initState(request EtcdRequest, response EtcdResponse) EtcdState {
func applyFailedRequest(states PossibleStates, request EtcdRequest) PossibleStates {
for _, s := range states {
newState, _ := applyRequestToSingleState(s, request)
states = append(states, newState)
if !reflect.DeepEqual(newState, s) {
states = append(states, newState)
}
}
return states
}
Expand Down Expand Up @@ -382,6 +394,8 @@ func applyRequestToSingleState(s EtcdState, request EtcdRequest) (EtcdState, Etc
s.Revision += 1
}
return s, EtcdResponse{Revision: s.Revision, LeaseRevoke: &LeaseRevokeResponse{}}
case Defragment:
return s, defragmentResponse()
default:
panic(fmt.Sprintf("Unknown request type: %v", request.Type))
}
Expand Down
58 changes: 58 additions & 0 deletions tests/linearizability/model/model_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,59 @@ func TestModelStep(t *testing.T) {
{req: leaseRevokeRequest(1), resp: leaseRevokeResponse(9)},
},
},
{
name: "All request types",
operations: []testOperation{
{req: leaseGrantRequest(1), resp: leaseGrantResponse(1)},
{req: putWithLeaseRequest("key", "1", 1), resp: putResponse(2)},
{req: leaseRevokeRequest(1), resp: leaseRevokeResponse(3)},
{req: putRequest("key", "4"), resp: putResponse(4)},
{req: getRequest("key"), resp: getResponse("4", 4)},
{req: txnRequest("key", "4", "5"), resp: txnResponse(true, 5)},
{req: deleteRequest("key"), resp: deleteResponse(1, 6)},
{req: defragmentRequest(), resp: defragmentResponse()},
},
},
{
name: "Defragment success between all other request types",
operations: []testOperation{
{req: defragmentRequest(), resp: defragmentResponse()},
{req: leaseGrantRequest(1), resp: leaseGrantResponse(1)},
{req: defragmentRequest(), resp: defragmentResponse()},
{req: putWithLeaseRequest("key", "1", 1), resp: putResponse(2)},
{req: defragmentRequest(), resp: defragmentResponse()},
{req: leaseRevokeRequest(1), resp: leaseRevokeResponse(3)},
{req: defragmentRequest(), resp: defragmentResponse()},
{req: putRequest("key", "4"), resp: putResponse(4)},
{req: defragmentRequest(), resp: defragmentResponse()},
{req: getRequest("key"), resp: getResponse("4", 4)},
{req: defragmentRequest(), resp: defragmentResponse()},
{req: txnRequest("key", "4", "5"), resp: txnResponse(true, 5)},
{req: defragmentRequest(), resp: defragmentResponse()},
{req: deleteRequest("key"), resp: deleteResponse(1, 6)},
{req: defragmentRequest(), resp: defragmentResponse()},
},
},
{
name: "Defragment failures between all other request types",
operations: []testOperation{
{req: defragmentRequest(), resp: failedResponse(errors.New("failed"))},
{req: leaseGrantRequest(1), resp: leaseGrantResponse(1)},
{req: defragmentRequest(), resp: failedResponse(errors.New("failed"))},
{req: putWithLeaseRequest("key", "1", 1), resp: putResponse(2)},
{req: defragmentRequest(), resp: failedResponse(errors.New("failed"))},
{req: leaseRevokeRequest(1), resp: leaseRevokeResponse(3)},
{req: defragmentRequest(), resp: failedResponse(errors.New("failed"))},
{req: putRequest("key", "4"), resp: putResponse(4)},
{req: defragmentRequest(), resp: failedResponse(errors.New("failed"))},
{req: getRequest("key"), resp: getResponse("4", 4)},
{req: defragmentRequest(), resp: failedResponse(errors.New("failed"))},
{req: txnRequest("key", "4", "5"), resp: txnResponse(true, 5)},
{req: defragmentRequest(), resp: failedResponse(errors.New("failed"))},
{req: deleteRequest("key"), resp: deleteResponse(1, 6)},
{req: defragmentRequest(), resp: failedResponse(errors.New("failed"))},
},
},
}
for _, tc := range tcs {
t.Run(tc.name, func(t *testing.T) {
Expand Down Expand Up @@ -604,6 +657,11 @@ func TestModelDescribe(t *testing.T) {
resp: failedResponse(errors.New("failed")),
expectDescribe: `if(key9=="9").then(put("key9", "99", nil)) -> err: "failed"`,
},
{
req: defragmentRequest(),
resp: defragmentResponse(),
expectDescribe: `defragment() -> ok`,
},
}
for _, tc := range tcs {
assert.Equal(t, tc.expectDescribe, Etcd.DescribeOperation(tc.req, tc.resp))
Expand Down
3 changes: 3 additions & 0 deletions tests/linearizability/traffic.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ const (
PutWithLease TrafficRequestType = "putWithLease"
LeaseRevoke TrafficRequestType = "leaseRevoke"
CompareAndSet TrafficRequestType = "compareAndSet"
Defragment TrafficRequestType = "defragment"
)

type Traffic interface {
Expand Down Expand Up @@ -124,6 +125,8 @@ func (t readWriteSingleKey) Write(ctx context.Context, c *recordingClient, limit
lm.RemoveLeaseId(cid)
}
}
case Defragment:
err = c.Defragment(writeCtx)
default:
panic("invalid operation")
}
Expand Down

0 comments on commit 91ec368

Please sign in to comment.