Merge pull request #14802 from geetasg/delete_api
linearizability tests - Add support for delete apidependabot/go_modules/go.uber.org/atomic-1.10.0
commit
2263315871
|
@ -89,3 +89,23 @@ func (c *recordingClient) Put(ctx context.Context, key, value string) error {
|
|||
})
|
||||
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()
|
||||
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, err: err},
|
||||
Return: returnTime.UnixNano(),
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -24,8 +24,9 @@ import (
|
|||
type Operation string
|
||||
|
||||
const (
|
||||
Get Operation = "get"
|
||||
Put Operation = "put"
|
||||
Get Operation = "get"
|
||||
Put Operation = "put"
|
||||
Delete Operation = "delete"
|
||||
)
|
||||
|
||||
type etcdRequest struct {
|
||||
|
@ -37,6 +38,7 @@ type etcdRequest struct {
|
|||
type etcdResponse struct {
|
||||
getData string
|
||||
revision int64
|
||||
deleted int64
|
||||
err error
|
||||
}
|
||||
|
||||
|
@ -78,6 +80,12 @@ var etcdModel = porcupine.Model{
|
|||
} else {
|
||||
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:
|
||||
return "<invalid>"
|
||||
}
|
||||
|
@ -99,6 +107,8 @@ func step(state EtcdState, request etcdRequest, response etcdResponse) (bool, Et
|
|||
return stepGet(state, request, response)
|
||||
case Put:
|
||||
return stepPut(state, request, response)
|
||||
case Delete:
|
||||
return stepDelete(state, request, response)
|
||||
default:
|
||||
panic("Unknown operation")
|
||||
}
|
||||
|
@ -119,6 +129,10 @@ func initState(request etcdRequest, response etcdResponse) EtcdState {
|
|||
} else {
|
||||
state.FailedWrites[request.putData] = struct{}{}
|
||||
}
|
||||
case Delete:
|
||||
if response.err != nil {
|
||||
state.FailedWrites[""] = struct{}{}
|
||||
}
|
||||
default:
|
||||
panic("Unknown operation")
|
||||
}
|
||||
|
@ -151,3 +165,29 @@ func stepPut(state EtcdState, request etcdRequest, response etcdResponse) (bool,
|
|||
state.LastRevision = response.revision
|
||||
return true, state
|
||||
}
|
||||
|
||||
func stepDelete(state EtcdState, request etcdRequest, response etcdResponse) (bool, EtcdState) {
|
||||
if response.err != nil {
|
||||
state.FailedWrites[""] = struct{}{}
|
||||
return true, state
|
||||
}
|
||||
deleteSucceeded := response.deleted != 0
|
||||
keySet := state.Value != ""
|
||||
|
||||
//non-existent key cannot be deleted.
|
||||
if deleteSucceeded != keySet {
|
||||
return false, state
|
||||
}
|
||||
//if key was deleted, response revision should go up
|
||||
if deleteSucceeded && state.LastRevision >= response.revision {
|
||||
return false, state
|
||||
}
|
||||
//if key was not deleted, response revision should not change
|
||||
if !deleteSucceeded && state.LastRevision != response.revision {
|
||||
return false, state
|
||||
}
|
||||
|
||||
state.Value = ""
|
||||
state.LastRevision = response.revision
|
||||
return true, state
|
||||
}
|
||||
|
|
|
@ -104,6 +104,16 @@ func TestModel(t *testing.T) {
|
|||
{req: etcdRequest{op: Put, key: "key", putData: "3"}, resp: etcdResponse{revision: 4}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Delete only increases revision on success",
|
||||
operations: []testOperation{
|
||||
{req: etcdRequest{op: Put, key: "key", putData: "1"}, resp: etcdResponse{revision: 1}},
|
||||
{req: etcdRequest{op: Delete, key: "key"}, resp: etcdResponse{deleted: 1, revision: 1}, failure: true},
|
||||
{req: etcdRequest{op: Delete, key: "key"}, resp: etcdResponse{deleted: 1, revision: 2}},
|
||||
{req: etcdRequest{op: Delete, key: "key"}, resp: etcdResponse{deleted: 0, revision: 3}, failure: true},
|
||||
{req: etcdRequest{op: Delete, key: "key"}, resp: etcdResponse{deleted: 0, revision: 2}},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tc := range tcs {
|
||||
var ok bool
|
||||
|
|
|
@ -24,7 +24,7 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
DefaultTraffic Traffic = readWriteSingleKey{key: "key", writes: []opChance{{operation: Put, chance: 100}}}
|
||||
DefaultTraffic Traffic = readWriteSingleKey{key: "key", writes: []opChance{{operation: Put, chance: 90}, {operation: Delete, chance: 10}}}
|
||||
)
|
||||
|
||||
type Traffic interface {
|
||||
|
@ -81,6 +81,8 @@ func (t readWriteSingleKey) Write(ctx context.Context, c *recordingClient, limit
|
|||
switch t.pickWriteOperation() {
|
||||
case Put:
|
||||
err = c.Put(putCtx, t.key, fmt.Sprintf("%d", id))
|
||||
case Delete:
|
||||
err = c.Delete(putCtx, t.key)
|
||||
default:
|
||||
panic("invalid operation")
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue