diff --git a/internal/app/coroutines/completePromise.go b/internal/app/coroutines/completePromise.go index ddd5561e..7a1c7968 100644 --- a/internal/app/coroutines/completePromise.go +++ b/internal/app/coroutines/completePromise.go @@ -165,8 +165,9 @@ func CompletePromise(metadata *metadata.Metadata, req *t_api.Request, res func(* } else { status := t_api.ForbiddenStatus(p.State) strict := req.CompletePromise.Strict && p.State != req.CompletePromise.State + timeout := !req.CompletePromise.Strict && p.State == promise.Timedout - if !strict && p.IdempotencyKeyForComplete.Match(req.CompletePromise.IdempotencyKey) { + if (!strict && p.IdempotencyKeyForComplete.Match(req.CompletePromise.IdempotencyKey)) || timeout { status = t_api.StatusOK } diff --git a/internal/app/subsystems/aio/store/postgres/postgres.go b/internal/app/subsystems/aio/store/postgres/postgres.go index f7f8a899..4446b18f 100644 --- a/internal/app/subsystems/aio/store/postgres/postgres.go +++ b/internal/app/subsystems/aio/store/postgres/postgres.go @@ -196,7 +196,7 @@ const ( UPDATE promises SET - state = 8, completed_on = timeout + state = 16, completed_on = timeout WHERE state = 1 AND timeout <= $1` diff --git a/internal/app/subsystems/aio/store/sqlite/sqlite.go b/internal/app/subsystems/aio/store/sqlite/sqlite.go index 5d9f5b47..6a5ce103 100644 --- a/internal/app/subsystems/aio/store/sqlite/sqlite.go +++ b/internal/app/subsystems/aio/store/sqlite/sqlite.go @@ -185,7 +185,7 @@ const ( UPDATE promises SET - state = 8, completed_on = timeout + state = 16, completed_on = timeout WHERE state = 1 AND timeout <= ?` diff --git a/internal/app/subsystems/aio/store/test/cases.go b/internal/app/subsystems/aio/store/test/cases.go index 7f24ffb7..c74dd6b4 100644 --- a/internal/app/subsystems/aio/store/test/cases.go +++ b/internal/app/subsystems/aio/store/test/cases.go @@ -1773,7 +1773,7 @@ var TestCases = []*testCase{ Kind: t_aio.UpdatePromise, UpdatePromise: &t_aio.UpdatePromiseCommand{ Id: "qux", - State: 8, + State: promise.Timedout, Value: promise.Value{ Headers: map[string]string{}, Data: []byte{}, @@ -1799,7 +1799,7 @@ var TestCases = []*testCase{ Kind: t_aio.UpdatePromise, UpdatePromise: &t_aio.UpdatePromiseCommand{ Id: "quy", - State: 16, + State: promise.Canceled, Value: promise.Value{ Headers: map[string]string{}, Data: []byte{}, @@ -1978,7 +1978,7 @@ var TestCases = []*testCase{ Records: []*promise.PromiseRecord{ { Id: "quy", - State: 16, + State: 8, ParamHeaders: []byte("{}"), ParamData: []byte{}, ValueHeaders: []byte("{}"), @@ -1991,7 +1991,7 @@ var TestCases = []*testCase{ }, { Id: "qux", - State: 8, + State: 16, ParamHeaders: []byte("{}"), ParamData: []byte{}, ValueHeaders: []byte("{}"), @@ -2026,7 +2026,7 @@ var TestCases = []*testCase{ Records: []*promise.PromiseRecord{ { Id: "quy", - State: 16, + State: 8, ParamHeaders: []byte("{}"), ParamData: []byte{}, ValueHeaders: []byte("{}"), @@ -2039,7 +2039,7 @@ var TestCases = []*testCase{ }, { Id: "qux", - State: 8, + State: 16, ParamHeaders: []byte("{}"), ParamData: []byte{}, ValueHeaders: []byte("{}"), @@ -3449,7 +3449,7 @@ var TestCases = []*testCase{ Records: []*promise.PromiseRecord{ { Id: "baz", - State: 8, + State: 16, ParamHeaders: []byte("{}"), ParamData: []byte{}, Timeout: 2, @@ -3460,7 +3460,7 @@ var TestCases = []*testCase{ }, { Id: "bar", - State: 8, + State: 16, ParamHeaders: []byte("{}"), ParamData: []byte{}, Timeout: 2, @@ -3471,7 +3471,7 @@ var TestCases = []*testCase{ }, { Id: "foo", - State: 8, + State: 16, ParamHeaders: []byte("{}"), ParamData: []byte{}, Timeout: 2, diff --git a/pkg/promise/promise.go b/pkg/promise/promise.go index ac3ad0ae..3c5fc2bf 100644 --- a/pkg/promise/promise.go +++ b/pkg/promise/promise.go @@ -42,8 +42,8 @@ const ( Pending State = 1 << iota Resolved Rejected - Timedout Canceled + Timedout ) func (s State) String() string { @@ -54,10 +54,10 @@ func (s State) String() string { return "RESOLVED" case Rejected: return "REJECTED" - case Timedout: - return "REJECTED_TIMEDOUT" case Canceled: return "REJECTED_CANCELED" + case Timedout: + return "REJECTED_TIMEDOUT" default: panic("invalid state") } @@ -80,10 +80,10 @@ func (s *State) UnmarshalJSON(data []byte) error { *s = Resolved case "REJECTED": *s = Rejected - case "REJECTED_TIMEDOUT": - *s = Timedout case "REJECTED_CANCELED": *s = Canceled + case "REJECTED_TIMEDOUT": + *s = Timedout default: return fmt.Errorf("invalid state '%s'", state) } diff --git a/test/dst/dst.go b/test/dst/dst.go index 68e664c3..4904377e 100644 --- a/test/dst/dst.go +++ b/test/dst/dst.go @@ -58,7 +58,7 @@ func (d *DST) Run(r *rand.Rand, api api.API, aio aio.AIO, system *system.System, generator.AddRequest(generator.GenerateCreatePromise) model.AddResponse(t_api.CreatePromise, model.ValidatCreatePromise) case t_api.CompletePromise: - generator.AddRequest(generator.GenerateCancelPromise) + generator.AddRequest(generator.GenerateCompletePromise) model.AddResponse(t_api.CompletePromise, model.ValidateCompletePromise) // SCHEDULES diff --git a/test/dst/generator.go b/test/dst/generator.go index de47d077..f76b7e83 100644 --- a/test/dst/generator.go +++ b/test/dst/generator.go @@ -2,6 +2,7 @@ package dst import ( "fmt" + "math" "math/rand" // nosemgrep "strconv" @@ -193,62 +194,19 @@ func (g *Generator) GenerateCreatePromise(r *rand.Rand, t int64) *t_api.Request } } -func (g *Generator) GenerateCancelPromise(r *rand.Rand, t int64) *t_api.Request { +func (g *Generator) GenerateCompletePromise(r *rand.Rand, t int64) *t_api.Request { id := g.idSet[r.Intn(len(g.idSet))] idempotencyKey := g.idemotencyKeySet[r.Intn(len(g.idemotencyKeySet))] data := g.dataSet[r.Intn(len(g.dataSet))] headers := g.headersSet[r.Intn(len(g.headersSet))] strict := r.Intn(2) == 0 + state := promise.State(math.Exp2(float64(r.Intn(3) + 1))) return &t_api.Request{ Kind: t_api.CompletePromise, CompletePromise: &t_api.CompletePromiseRequest{ Id: id, - State: promise.Canceled, - Value: promise.Value{ - Headers: headers, - Data: data, - }, - IdempotencyKey: idempotencyKey, - Strict: strict, - }, - } -} - -func (g *Generator) GenerateResolvePromise(r *rand.Rand, t int64) *t_api.Request { - id := g.idSet[r.Intn(len(g.idSet))] - idempotencyKey := g.idemotencyKeySet[r.Intn(len(g.idemotencyKeySet))] - data := g.dataSet[r.Intn(len(g.dataSet))] - headers := g.headersSet[r.Intn(len(g.headersSet))] - strict := r.Intn(2) == 0 - - return &t_api.Request{ - Kind: t_api.CompletePromise, - CompletePromise: &t_api.CompletePromiseRequest{ - Id: id, - State: promise.Resolved, - Value: promise.Value{ - Headers: headers, - Data: data, - }, - IdempotencyKey: idempotencyKey, - Strict: strict, - }, - } -} - -func (g *Generator) GenerateRejectPromise(r *rand.Rand, t int64) *t_api.Request { - id := g.idSet[r.Intn(len(g.idSet))] - idempotencyKey := g.idemotencyKeySet[r.Intn(len(g.idemotencyKeySet))] - data := g.dataSet[r.Intn(len(g.dataSet))] - headers := g.headersSet[r.Intn(len(g.headersSet))] - strict := r.Intn(2) == 0 - - return &t_api.Request{ - Kind: t_api.CompletePromise, - CompletePromise: &t_api.CompletePromiseRequest{ - Id: id, - State: promise.Rejected, + State: state, Value: promise.Value{ Headers: headers, Data: data, diff --git a/test/dst/model.go b/test/dst/model.go index 94649053..048e56df 100644 --- a/test/dst/model.go +++ b/test/dst/model.go @@ -252,9 +252,10 @@ func (m *Model) ValidateCompletePromise(t int64, req *t_api.Request, res *t_api. switch res.CompletePromise.Status { case t_api.StatusOK: if pm.completed() { - if !pm.idempotencyKeyForCompleteMatch(res.CompletePromise.Promise) { + if !pm.idempotencyKeyForCompleteMatch(res.CompletePromise.Promise) && + (req.CompletePromise.Strict || pm.promise.State != promise.Timedout) { return fmt.Errorf("ikey mismatch (%s, %s)", pm.promise.IdempotencyKeyForComplete, res.CompletePromise.Promise.IdempotencyKeyForComplete) - } else if req.CompletePromise.Strict && pm.promise.State != promise.Canceled { + } else if req.CompletePromise.Strict && pm.promise.State != req.CompletePromise.State { return fmt.Errorf("unexpected state %s when strict true", pm.promise.State) } } @@ -268,7 +269,13 @@ func (m *Model) ValidateCompletePromise(t int64, req *t_api.Request, res *t_api. pm.promise = res.CompletePromise.Promise return nil case t_api.StatusCreated: - if res.CompletePromise.Promise.State != promise.Canceled { + if req.CompletePromise.State == promise.Resolved && res.CompletePromise.Promise.State != promise.Resolved { + return fmt.Errorf("unexpected state %s after resolve promise", res.CompletePromise.Promise.State) + } + if req.CompletePromise.State == promise.Rejected && res.CompletePromise.Promise.State != promise.Rejected { + return fmt.Errorf("unexpected state %s after reject promise", res.CompletePromise.Promise.State) + } + if req.CompletePromise.State == promise.Canceled && res.CompletePromise.Promise.State != promise.Canceled { return fmt.Errorf("unexpected state %s after cancel promise", res.CompletePromise.Promise.State) } if pm.completed() {