From eab8a8e4f1e401bb3850ffaa111560bac985b020 Mon Sep 17 00:00:00 2001 From: Alex Shtin Date: Tue, 14 May 2024 17:24:26 -0700 Subject: [PATCH] Add test for non-durable updates shiped on WFT with events --- features/features.go | 3 + .../non_durable_reject_with_events/README.md | 12 +++ .../non_durable_reject_with_events/feature.go | 92 +++++++++++++++++++ 3 files changed, 107 insertions(+) create mode 100644 features/update/non_durable_reject_with_events/README.md create mode 100644 features/update/non_durable_reject_with_events/feature.go diff --git a/features/features.go b/features/features.go index 82f2862f..d88d1885 100644 --- a/features/features.go +++ b/features/features.go @@ -2,6 +2,7 @@ package features import ( activity_basic_no_workflow_timeout "github.com/temporalio/features/features/activity/basic_no_workflow_timeout" + activity_cancel_try_cancel "github.com/temporalio/features/features/activity/cancel_try_cancel" activity_retry_on_error "github.com/temporalio/features/features/activity/retry_on_error" bugs_go_activity_start_race "github.com/temporalio/features/features/bugs/go/activity_start_race" @@ -45,6 +46,7 @@ import ( update_client_interceptor "github.com/temporalio/features/features/update/client_interceptor" update_deduplication "github.com/temporalio/features/features/update/deduplication" update_non_durable_reject "github.com/temporalio/features/features/update/non_durable_reject" + update_non_durable_reject_with_events "github.com/temporalio/features/features/update/non_durable_reject_with_events" update_self "github.com/temporalio/features/features/update/self" update_task_failure "github.com/temporalio/features/features/update/task_failure" update_validation_replay "github.com/temporalio/features/features/update/validation_replay" @@ -99,6 +101,7 @@ func init() { update_deduplication.Feature, update_client_interceptor.Feature, update_non_durable_reject.Feature, + update_non_durable_reject_with_events.Feature, update_self.Feature, update_task_failure.Feature, update_validation_replay.Feature, diff --git a/features/update/non_durable_reject_with_events/README.md b/features/update/non_durable_reject_with_events/README.md new file mode 100644 index 00000000..77b03bef --- /dev/null +++ b/features/update/non_durable_reject_with_events/README.md @@ -0,0 +1,12 @@ +# Update Rejections Do Not Take Up Space In History + +When updates are registered with a validation handler and that validation +handler rejects an update, neither the invocation of the update nor the +rejection appear as part of the workflow's history. + +# Detailed spec + +Update requests don't hit history until they've passed validation on a worker. +Update rejections never hit history. If a workflow task carries _only_ update +requests and all of those requests are rejected, the events related to that +workflow task (i.e. Scheduled/Started/Completed) do not land in history. diff --git a/features/update/non_durable_reject_with_events/feature.go b/features/update/non_durable_reject_with_events/feature.go new file mode 100644 index 00000000..83062000 --- /dev/null +++ b/features/update/non_durable_reject_with_events/feature.go @@ -0,0 +1,92 @@ +package non_durable_reject + +import ( + "context" + "fmt" + "time" + + "go.temporal.io/sdk/client" + "go.temporal.io/sdk/workflow" + + "github.com/temporalio/features/features/update/updateutil" + "github.com/temporalio/features/harness/go/harness" +) + +const ( + step = 2 + count = 5 + updateAdd = "updateActivity" +) + +var Feature = harness.Feature{ + Workflows: NonDurableReject, + ExpectRunResult: step * count, + Execute: func(ctx context.Context, runner *harness.Runner) (client.WorkflowRun, error) { + if reason := updateutil.CheckServerSupportsUpdate(ctx, runner.Client); reason != "" { + return nil, runner.Skip(reason) + } + run, err := runner.ExecuteDefault(ctx) + if err != nil { + return nil, err + } + + handle, err := runner.Client.UpdateWorkflow( + ctx, + run.GetID(), + run.GetRunID(), + updateAdd, + -1, + ) + runner.Require.NoError(err) + var result int + runner.Require.Error(handle.Get(ctx, &result), "expected negative value to be rejected") + + for i := 0; i < count; i++ { + handle, err := runner.Client.UpdateWorkflow( + ctx, + run.GetID(), + run.GetRunID(), + updateAdd, + step, + ) + runner.Require.NoError(err) + runner.Require.NoError(handle.Get(ctx, &result), "expected non-negative value to be accepted") + + handle, err = runner.Client.UpdateWorkflow( + ctx, + run.GetID(), + run.GetRunID(), + updateAdd, + -1, + ) + runner.Require.NoError(err) + runner.Require.Error(handle.Get(ctx, &result), "expected negative value to be rejected") + } + + updateutil.RequireNoUpdateRejectedEvents(ctx, runner) + return run, nil + }, +} + +func nonNegative(i int) error { + if i < 0 { + return fmt.Errorf("expected non-negative value (%v)", i) + } + return nil +} + +func NonDurableReject(ctx workflow.Context) (int, error) { + counter := 0 + if err := workflow.SetUpdateHandlerWithOptions(ctx, updateAdd, + func(ctx workflow.Context, i int) (int, error) { + counter += i + return counter, nil + }, + workflow.UpdateHandlerOptions{Validator: nonNegative}, + ); err != nil { + return 0, err + } + + _ = workflow.Sleep(ctx, 4*time.Second) + return counter, nil +}