linearizability tests - Add support for delete api

Signed-off-by: Geeta Gharpure <geetagh@amazon.com>
This commit is contained in:
Geeta Gharpure 2022-11-17 14:19:04 -08:00
parent 6a156bd555
commit de991a9f2d
3 changed files with 62 additions and 2 deletions

View File

@ -89,3 +89,26 @@ func (c *recordingClient) Put(ctx context.Context, key, value string) error {
}) })
return nil return nil
} }
func (c *recordingClient) Delete(ctx context.Context, key string) error {
callTime := time.Now()
resp, err := c.client.Delete(ctx, key)
returnTime := time.Now()
if err != nil {
return err
}
var revision int64
var deleted int64
if resp != nil && resp.Header != nil {
revision = resp.Header.Revision
deleted = resp.Deleted
}
c.operations = append(c.operations, porcupine.Operation{
ClientId: c.id,
Input: etcdRequest{op: Delete, key: key},
Call: callTime.UnixNano(),
Output: etcdResponse{revision: revision, deleted: deleted},
Return: returnTime.UnixNano(),
})
return nil
}

View File

@ -24,8 +24,9 @@ import (
type Operation string type Operation string
const ( const (
Get Operation = "get" Get Operation = "get"
Put Operation = "put" Put Operation = "put"
Delete Operation = "delete"
) )
type etcdRequest struct { type etcdRequest struct {
@ -37,6 +38,7 @@ type etcdRequest struct {
type etcdResponse struct { type etcdResponse struct {
getData string getData string
revision int64 revision int64
deleted int64
err error err error
} }
@ -78,6 +80,12 @@ var etcdModel = porcupine.Model{
} else { } else {
return fmt.Sprintf("put(%q, %q) -> ok, rev: %d", request.key, request.putData, response.revision) return fmt.Sprintf("put(%q, %q) -> ok, rev: %d", request.key, request.putData, response.revision)
} }
case Delete:
if response.err != nil {
return fmt.Sprintf("delete(%q) -> %s", request.key, response.err)
} else {
return fmt.Sprintf("delete(%q) -> ok, rev: %d deleted:%d", request.key, response.revision, response.deleted)
}
default: default:
return "<invalid>" return "<invalid>"
} }
@ -99,6 +107,8 @@ func step(state EtcdState, request etcdRequest, response etcdResponse) (bool, Et
return stepGet(state, request, response) return stepGet(state, request, response)
case Put: case Put:
return stepPut(state, request, response) return stepPut(state, request, response)
case Delete:
return stepDelete(state, request, response)
default: default:
panic("Unknown operation") panic("Unknown operation")
} }
@ -119,6 +129,9 @@ func initState(request etcdRequest, response etcdResponse) EtcdState {
} else { } else {
state.FailedWrites[request.putData] = struct{}{} state.FailedWrites[request.putData] = struct{}{}
} }
case Delete:
//TODO should state have 'deleted' field?
state.Value = ""
default: default:
panic("Unknown operation") panic("Unknown operation")
} }
@ -151,3 +164,15 @@ func stepPut(state EtcdState, request etcdRequest, response etcdResponse) (bool,
state.LastRevision = response.revision state.LastRevision = response.revision
return true, state return true, state
} }
func stepDelete(state EtcdState, request etcdRequest, response etcdResponse) (bool, EtcdState) {
if response.err != nil {
state.FailedWrites[request.putData] = struct{}{}
return true, state
}
if response.deleted == 1 && state.LastRevision >= response.revision {
return false, state
}
state.LastRevision = response.revision
return true, state
}

View File

@ -104,6 +104,18 @@ func TestModel(t *testing.T) {
{req: etcdRequest{op: Put, key: "key", putData: "3"}, resp: etcdResponse{revision: 4}}, {req: etcdRequest{op: Put, key: "key", putData: "3"}, resp: etcdResponse{revision: 4}},
}, },
}, },
{
name: "Deleting non existent key does not change revision",
operations: []testOperation{
{req: etcdRequest{op: Delete, key: "NotThere"}, resp: etcdResponse{deleted: 0, revision: 4}},
},
},
{
name: "Deleting existent key bumps up revision",
operations: []testOperation{
{req: etcdRequest{op: Delete, key: "key"}, resp: etcdResponse{deleted: 1, revision: 5}},
},
},
} }
for _, tc := range tcs { for _, tc := range tcs {
var ok bool var ok bool