tests: Add comments to linearizability functions

Signed-off-by: Marek Siarkowicz <siarkowicz@google.com>
Co-authored-by: Benjamin Wang <wachao@vmware.com>
This commit is contained in:
Marek Siarkowicz 2022-12-28 21:39:00 +01:00
parent ff71f34368
commit d4c8611be9

View File

@ -46,15 +46,19 @@ type EtcdResponse struct {
Err error Err error
} }
type PossibleStates []EtcdState
type EtcdState struct { type EtcdState struct {
Revision int64 Revision int64
KeyValues map[string]string KeyValues map[string]string
} }
var etcdModel = porcupine.Model{ var etcdModel = porcupine.Model{
Init: func() interface{} { return "[]" }, Init: func() interface{} {
return "[]" // empty PossibleStates
},
Step: func(st interface{}, in interface{}, out interface{}) (bool, interface{}) { Step: func(st interface{}, in interface{}, out interface{}) (bool, interface{}) {
var states []EtcdState var states PossibleStates
err := json.Unmarshal([]byte(st.(string)), &states) err := json.Unmarshal([]byte(st.(string)), &states)
if err != nil { if err != nil {
panic(err) panic(err)
@ -100,44 +104,24 @@ var etcdModel = porcupine.Model{
}, },
} }
func step(states []EtcdState, request EtcdRequest, response EtcdResponse) (bool, []EtcdState) { func step(states PossibleStates, request EtcdRequest, response EtcdResponse) (bool, PossibleStates) {
if len(states) == 0 { if len(states) == 0 {
return true, initStates(request, response) // states were not initialized
if response.Err != nil {
return true, nil
}
return true, PossibleStates{initState(request, response)}
} }
if response.Err != nil { if response.Err != nil {
// Add addition states for failed request in case of failed request was persisted. states = applyFailedRequest(states, request)
states = append(states, applyRequest(states, request)...)
} else { } else {
// Remove states that didn't lead to response we got. states = applyRequest(states, request, response)
states = filterStateMatchesResponse(states, request, response)
} }
return len(states) > 0, states return len(states) > 0, states
} }
func applyRequest(states []EtcdState, request EtcdRequest) []EtcdState { // initState tries to create etcd state based on the first request.
newStates := make([]EtcdState, 0, len(states)) func initState(request EtcdRequest, response EtcdResponse) EtcdState {
for _, s := range states {
newState, _ := stepState(s, request)
newStates = append(newStates, newState)
}
return newStates
}
func filterStateMatchesResponse(states []EtcdState, request EtcdRequest, response EtcdResponse) []EtcdState {
newStates := make([]EtcdState, 0, len(states))
for _, s := range states {
newState, expectResponse := stepState(s, request)
if expectResponse == response {
newStates = append(newStates, newState)
}
}
return newStates
}
func initStates(request EtcdRequest, response EtcdResponse) []EtcdState {
if response.Err != nil {
return []EtcdState{}
}
state := EtcdState{ state := EtcdState{
Revision: response.Revision, Revision: response.Revision,
KeyValues: map[string]string{}, KeyValues: map[string]string{},
@ -154,14 +138,35 @@ func initStates(request EtcdRequest, response EtcdResponse) []EtcdState {
if response.TxnSucceeded { if response.TxnSucceeded {
state.KeyValues[request.Key] = request.TxnNewData state.KeyValues[request.Key] = request.TxnNewData
} }
return []EtcdState{}
default: default:
panic("Unknown operation") panic("Unknown operation")
} }
return []EtcdState{state} return state
} }
func stepState(s EtcdState, request EtcdRequest) (EtcdState, EtcdResponse) { // applyFailedRequest handles a failed requests, one that it's not known if it was persisted or not.
func applyFailedRequest(states PossibleStates, request EtcdRequest) PossibleStates {
for _, s := range states {
newState, _ := applyRequestToSingleState(s, request)
states = append(states, newState)
}
return states
}
// applyRequest handles a successful request by applying it to possible states and checking if they match the response.
func applyRequest(states PossibleStates, request EtcdRequest, response EtcdResponse) PossibleStates {
newStates := make(PossibleStates, 0, len(states))
for _, s := range states {
newState, expectResponse := applyRequestToSingleState(s, request)
if expectResponse == response {
newStates = append(newStates, newState)
}
}
return newStates
}
// applyRequestToSingleState handles a successful request, returning updated state and response it would generate.
func applyRequestToSingleState(s EtcdState, request EtcdRequest) (EtcdState, EtcdResponse) {
newKVs := map[string]string{} newKVs := map[string]string{}
for k, v := range s.KeyValues { for k, v := range s.KeyValues {
newKVs[k] = v newKVs[k] = v