diff --git a/Makefile b/Makefile index 684900b1a1d..ad25d70ccb7 100644 --- a/Makefile +++ b/Makefile @@ -204,9 +204,17 @@ $(BUF): | $(LOCALBIN) GO_API_VER = $(shell go list -m -f '{{.Version}}' go.temporal.io/api \ || (echo "failed to fetch version for go.temporal.io/api" >&2)) +GO_API_REPLACED = $(shell go list -m -f '{{if .Replace}}true{{end}}' go.temporal.io/api \ + || (echo "failed to resolve replacement for go.temporal.io/api" >&2)) PROTOGEN := $(LOCALBIN)/protogen-$(GO_API_VER) +ifeq ($(GO_API_REPLACED),true) +$(PROTOGEN): | $(LOCALBIN) + @printf $(COLOR) "Building protogen through the replaced go.temporal.io/api module..." + @go build -o $@ go.temporal.io/api/cmd/protogen +else $(PROTOGEN): | $(LOCALBIN) $(call go-install-tool,$(PROTOGEN),go.temporal.io/api/cmd/protogen,$(GO_API_VER)) +endif ACTIONLINT_VER := v1.7.7 ACTIONLINT := $(LOCALBIN)/actionlint-$(ACTIONLINT_VER) @@ -285,8 +293,14 @@ $(STAMPDIR)/protoc-gen-go-grpc-$(PROTOC_GEN_GO_GRPC_VER): | $(STAMPDIR) $(LOCALB $(PROTOC_GEN_GO_GRPC): $(STAMPDIR)/protoc-gen-go-grpc-$(PROTOC_GEN_GO_GRPC_VER) PROTOC_GEN_GO_HELPERS := $(LOCALBIN)/protoc-gen-go-helpers-$(GO_API_VER) +ifeq ($(GO_API_REPLACED),true) +$(STAMPDIR)/protoc-gen-go-helpers-$(GO_API_VER): | $(STAMPDIR) $(LOCALBIN) + @printf $(COLOR) "Building protoc-gen-go-helpers through the replaced go.temporal.io/api module..." + @go build -o $(PROTOC_GEN_GO_HELPERS) go.temporal.io/api/cmd/protoc-gen-go-helpers +else $(STAMPDIR)/protoc-gen-go-helpers-$(GO_API_VER): | $(STAMPDIR) $(LOCALBIN) $(call go-install-tool,$(PROTOC_GEN_GO_HELPERS),go.temporal.io/api/cmd/protoc-gen-go-helpers,$(GO_API_VER)) +endif @touch $@ $(PROTOC_GEN_GO_HELPERS): $(STAMPDIR)/protoc-gen-go-helpers-$(GO_API_VER) diff --git a/chasm/lib/activity/config.go b/chasm/lib/activity/config.go index a6551f49dd7..d883d58c3a5 100644 --- a/chasm/lib/activity/config.go +++ b/chasm/lib/activity/config.go @@ -35,6 +35,12 @@ var ( `Allows non-zero start_delay on StartActivityExecution requests.`, ) + EnableEagerStart = dynamicconfig.NewNamespaceBoolSetting( + "activity.enableEagerStart", + true, + `Allows the first standalone activity task to be returned directly by StartActivityExecution.`, + ) + EnableCallbacks = dynamicconfig.NewNamespaceBoolSetting( "activity.enableCallbacks", false, @@ -63,6 +69,7 @@ type Config struct { EnableCallbacks dynamicconfig.BoolPropertyFnWithNamespaceFilter EnabledCallbackKinds dynamicconfig.TypedPropertyFnWithNamespaceFilter[[]callbacks.Kind] Enabled dynamicconfig.BoolPropertyFnWithNamespaceFilter + EnableEagerStart dynamicconfig.BoolPropertyFnWithNamespaceFilter EnableStandaloneActivityOperatorCommands dynamicconfig.BoolPropertyFnWithNamespaceFilter LongPollBuffer dynamicconfig.DurationPropertyFnWithNamespaceFilter LongPollTimeout dynamicconfig.DurationPropertyFnWithNamespaceFilter @@ -85,6 +92,7 @@ func ConfigProvider(dc *dynamicconfig.Collection) *Config { EnableCallbacks: EnableCallbacks.Get(dc), EnabledCallbackKinds: EnabledCallbackKinds.Get(dc), Enabled: Enabled.Get(dc), + EnableEagerStart: EnableEagerStart.Get(dc), EnableStandaloneActivityOperatorCommands: EnableStandaloneActivityOperatorCommands.Get(dc), LongPollBuffer: LongPollBuffer.Get(dc), LongPollTimeout: LongPollTimeout.Get(dc), diff --git a/chasm/lib/activity/frontend.go b/chasm/lib/activity/frontend.go index 3bf32ec5964..314701fea24 100644 --- a/chasm/lib/activity/frontend.go +++ b/chasm/lib/activity/frontend.go @@ -42,6 +42,11 @@ var ErrStandaloneActivityDisabled = serviceerror.NewUnimplemented("Standalone ac var ErrStandaloneActivityOperatorCommandsDisabled = serviceerror.NewUnimplemented("Standalone activity operator commands are disabled") +const ( + eagerStartDeniedReasonDynamicConfigDisabled metrics.ReasonString = "dynamic_config_disabled" + eagerStartDeniedReasonStartDelay metrics.ReasonString = "start_delay" +) + type frontendHandler struct { FrontendHandler callbackValidator callbacks.Validator @@ -386,7 +391,24 @@ func (h *frontendHandler) validateAndPopulateStartRequest( if req.GetStartDelay().AsDuration() > 0 && !h.config.StartDelayEnabled(req.GetNamespace()) { return nil, serviceerror.NewInvalidArgument("start_delay is not enabled for this namespace") } - // TODO(saa): when eager start is supported, deny it if start delay > 0 (same as workflow behavior). + if req.GetRequestEagerExecution() { + metricsHandler := h.metricsHandler.WithTags( + metrics.NamespaceTag(req.GetNamespace()), + metrics.OperationTag("StartActivityExecution"), + ) + switch { + case !h.config.EnableEagerStart(req.GetNamespace()): + metrics.StandaloneActivityEagerStartDeniedCounter.With(metricsHandler). + Record(1, metrics.ReasonTag(eagerStartDeniedReasonDynamicConfigDisabled)) + req.RequestEagerExecution = false + case req.GetStartDelay().AsDuration() > 0: + metrics.StandaloneActivityEagerStartDeniedCounter.With(metricsHandler). + Record(1, metrics.ReasonTag(eagerStartDeniedReasonStartDelay)) + req.RequestEagerExecution = false + default: + metrics.StandaloneActivityEagerStartAcceptedCounter.With(metricsHandler).Record(1) + } + } opts := activityOptionsFromStartRequest(req) err := ValidateAndNormalizeStandaloneActivity( diff --git a/chasm/lib/activity/frontend_test.go b/chasm/lib/activity/frontend_test.go index 1e105364bf4..ab20e9bf504 100644 --- a/chasm/lib/activity/frontend_test.go +++ b/chasm/lib/activity/frontend_test.go @@ -11,6 +11,8 @@ import ( "go.temporal.io/api/workflowservice/v1" "go.temporal.io/server/common/dynamicconfig" "go.temporal.io/server/common/log" + "go.temporal.io/server/common/metrics" + "go.temporal.io/server/common/metrics/metricstest" "go.temporal.io/server/common/namespace" "google.golang.org/protobuf/types/known/durationpb" ) @@ -215,3 +217,82 @@ func TestRequestIdStableAcrossRetries(t *testing.T) { }) }) } + +func TestEagerStartFallback(t *testing.T) { + newHandler := func(eagerEnabled bool, metricsHandler metrics.Handler) *frontendHandler { + return &frontendHandler{ + config: &Config{ + BlobSizeLimitError: defaultBlobSizeLimitError, + BlobSizeLimitWarn: defaultBlobSizeLimitWarn, + DefaultActivityRetryPolicy: getDefaultRetrySettings, + EnableEagerStart: dynamicconfig.GetBoolPropertyFnFilteredByNamespace(eagerEnabled), + MaxIDLengthLimit: func() int { return defaultMaxIDLengthLimit }, + MaxUserMetadataDetailsSize: defaultMaxUserMetadataDetailsSize, + MaxUserMetadataSummarySize: defaultMaxUserMetadataSummarySize, + StartDelayEnabled: dynamicconfig.GetBoolPropertyFnFilteredByNamespace(true), + }, + linkValidator: newLinkValidator( + defaultMaxLinksPerRequest, + func(string) int { return 2000 }, + defaultLinkMaxSize, + ), + logger: log.NewNoopLogger(), + metricsHandler: metricsHandler, + } + } + + newRequest := func() *workflowservice.StartActivityExecutionRequest { + return &workflowservice.StartActivityExecutionRequest{ + Namespace: "test-namespace", + RequestEagerExecution: true, + ActivityId: "test-activity", + ActivityType: &commonpb.ActivityType{Name: "test-type"}, + TaskQueue: &taskqueuepb.TaskQueue{Name: "test-queue"}, + StartToCloseTimeout: durationpb.New(time.Minute), + } + } + + assertMetric := func(t *testing.T, capture *metricstest.Capture, metricName string, reason string) { + t.Helper() + recordings := capture.SnapshotMetric(metricName) + require.Len(t, recordings, 1) + if reason != "" { + require.Equal(t, reason, recordings[0].Tags["reason"]) + } + } + + t.Run("enabled", func(t *testing.T) { + metricsHandler := metricstest.NewCaptureHandler() + capture := metricsHandler.StartCapture() + defer metricsHandler.StopCapture(capture) + + req, err := newHandler(true, metricsHandler).validateAndPopulateStartRequest(context.Background(), newRequest(), "test-namespace-id") + require.NoError(t, err) + require.True(t, req.GetRequestEagerExecution()) + assertMetric(t, capture, metrics.StandaloneActivityEagerStartAcceptedCounter.Name(), "") + }) + + t.Run("namespace disabled", func(t *testing.T) { + metricsHandler := metricstest.NewCaptureHandler() + capture := metricsHandler.StartCapture() + defer metricsHandler.StopCapture(capture) + + req, err := newHandler(false, metricsHandler).validateAndPopulateStartRequest(context.Background(), newRequest(), "test-namespace-id") + require.NoError(t, err) + require.False(t, req.GetRequestEagerExecution()) + assertMetric(t, capture, metrics.StandaloneActivityEagerStartDeniedCounter.Name(), "dynamic_config_disabled") + }) + + t.Run("start delay", func(t *testing.T) { + metricsHandler := metricstest.NewCaptureHandler() + capture := metricsHandler.StartCapture() + defer metricsHandler.StopCapture(capture) + + req := newRequest() + req.StartDelay = durationpb.New(time.Minute) + req, err := newHandler(true, metricsHandler).validateAndPopulateStartRequest(context.Background(), req, "test-namespace-id") + require.NoError(t, err) + require.False(t, req.GetRequestEagerExecution()) + assertMetric(t, capture, metrics.StandaloneActivityEagerStartDeniedCounter.Name(), "start_delay") + }) +} diff --git a/chasm/lib/activity/handler.go b/chasm/lib/activity/handler.go index a89d7934d2a..79555e937ae 100644 --- a/chasm/lib/activity/handler.go +++ b/chasm/lib/activity/handler.go @@ -99,7 +99,14 @@ func (h *handler) StartActivityExecution(ctx context.Context, req *activitypb.St } } - err = TransitionScheduled.Apply(newActivity, mutableContext, nil) + if request.GetRequestEagerExecution() { + err = TransitionEagerStarted.Apply(newActivity, mutableContext, eagerStartEvent{ + requestID: request.GetRequestId(), + identity: request.GetIdentity(), + }) + } else { + err = TransitionScheduled.Apply(newActivity, mutableContext, nil) + } if err != nil { return nil, err } @@ -129,6 +136,23 @@ func (h *handler) StartActivityExecution(ctx context.Context, req *activitypb.St ) } + var eagerTask *workflowservice.PollActivityTaskQueueResponse + if result.Created && frontendReq.GetRequestEagerExecution() { + eagerTask, err = chasm.ReadComponent( + ctx, + result.ExecutionRef, + (*Activity).buildEagerActivityTask, + eagerActivityTaskRequest{ + namespaceID: req.GetNamespaceId(), + namespace: frontendReq.GetNamespace(), + requestID: frontendReq.GetRequestId(), + }, + ) + if err != nil { + return nil, err + } + } + // Apply on_conflict_options to an existing activity. // TODO: Use chasm.UpdateWithStartExecution to avoid a second transaction once the engine supports BusinessIDConflictPolicyFail in the updateFn path. cbs := frontendReq.GetCompletionCallbacks() @@ -165,8 +189,9 @@ func (h *handler) StartActivityExecution(ctx context.Context, req *activitypb.St return &activitypb.StartActivityExecutionResponse{ FrontendResponse: &workflowservice.StartActivityExecutionResponse{ - RunId: result.ExecutionKey.RunID, - Started: result.Created, + RunId: result.ExecutionKey.RunID, + Started: result.Created, + EagerActivityTask: eagerTask, Link: &commonpb.Link{ Variant: &commonpb.Link_Activity_{ Activity: &commonpb.Link_Activity{ @@ -176,7 +201,6 @@ func (h *handler) StartActivityExecution(ctx context.Context, req *activitypb.St }, }, }, - // EagerTask: TODO when supported, need to call the same code that would handle the HandleStarted API }, }, nil } diff --git a/chasm/lib/activity/model/model.go b/chasm/lib/activity/model/model.go index a104b18a1c8..c27d0b4af45 100644 --- a/chasm/lib/activity/model/model.go +++ b/chasm/lib/activity/model/model.go @@ -33,6 +33,10 @@ type Outcome struct { // Initial is the state of a newly created Activity. func Initial(cfg Config) AbstractState { s := AbstractState{Status: Scheduled, AttemptCount: 1} + if cfg.InitialAttemptStarted { + s.Status = Started + return s + } if cfg.HasStartDelay { s.Dispatchability = StartDelayPending } diff --git a/chasm/lib/activity/model/model_test.go b/chasm/lib/activity/model/model_test.go index 39187e104d5..54c82e1a407 100644 --- a/chasm/lib/activity/model/model_test.go +++ b/chasm/lib/activity/model/model_test.go @@ -12,6 +12,27 @@ func TestInitial(t *testing.T) { require.Equal(t, AbstractState{Status: Scheduled, AttemptCount: 1}, Initial(Config{HasScheduleToClose: true})) } +func TestEagerInitialAttempt(t *testing.T) { + cfg := Config{ + InitialAttemptStarted: true, + HasScheduleToClose: true, + HasScheduleToStart: true, + HasHeartbeat: true, + } + initial := Initial(cfg) + require.Equal(t, AbstractState{Status: Started, AttemptCount: 1}, initial) + require.False(t, FindsTask(initial), "an eager task is already started and must not be dispatched again") + require.False(t, Possible(cfg, initial, ScheduleToStartElapsesType), "an eager first attempt has no schedule-to-start window") + require.True(t, Possible(cfg, initial, ScheduleToCloseElapsesType), "schedule-to-close still protects an eager first attempt") + require.True(t, Possible(cfg, initial, StartToCloseElapsesType), "start-to-close still protects an eager first attempt") + require.True(t, Possible(cfg, initial, HeartbeatElapsesType), "heartbeat still protects an eager first attempt") + + retry := Transition(cfg, initial, FailRetryably).Next + require.Equal(t, AbstractState{Status: Scheduled, AttemptCount: 2, Dispatchability: BackoffPending}, retry) + retry = Transition(cfg, retry, BackoffElapses).Next + require.True(t, FindsTask(retry), "an eager first attempt's retry must return to normal dispatch") +} + func TestPollFromScheduledStarts(t *testing.T) { out := Transition(Config{}, Initial(Config{}), Event{Type: PollType}) require.Equal(t, NoError, out.Reject) @@ -40,7 +61,10 @@ func TestPauseWhileStartedIsPauseRequested(t *testing.T) { // backedOffRetry returns a Scheduled state with a pending retry backoff (attempt 2), reached the way // a worker would: poll the first attempt, then fail it retryably. func backedOffRetry(t require.TestingT, cfg Config) AbstractState { - started := Transition(cfg, Initial(cfg), Event{Type: PollType}).Next + started := Initial(cfg) + if started.Status != Started { + started = Transition(cfg, started, Event{Type: PollType}).Next + } s := Transition(cfg, started, Event{Type: RespondFailedType, Failure: &Failure{}}).Next require.Equal(t, Scheduled, s.Status, "a retryable failure must schedule a retry") require.Equal(t, BackoffPending, s.Dispatchability, "a retry must wait for its backoff") diff --git a/chasm/lib/activity/model/vocabulary.go b/chasm/lib/activity/model/vocabulary.go index 02e646a090e..e00c19ca20e 100644 --- a/chasm/lib/activity/model/vocabulary.go +++ b/chasm/lib/activity/model/vocabulary.go @@ -43,11 +43,13 @@ type AbstractState struct { // Config is what the model needs to know about an activity's configuration: which options are set, // and what their durations imply regarding retries. type Config struct { - HasScheduleToClose bool - HasScheduleToStart bool - HasHeartbeat bool - HasStartDelay bool - MaxAttempts int32 // 0 = unlimited + // InitialAttemptStarted means the first attempt was delivered eagerly and is already running. + InitialAttemptStarted bool + HasScheduleToClose bool + HasScheduleToStart bool + HasHeartbeat bool + HasStartDelay bool + MaxAttempts int32 // 0 = unlimited // NonRetryableTimeouts are the timeout elapses whose failure the retry policy refuses to retry, // so that the timeout closes the activity instead of scheduling another attempt. diff --git a/chasm/lib/activity/responses.go b/chasm/lib/activity/responses.go index 795f3ed89c5..4e3ed739dae 100644 --- a/chasm/lib/activity/responses.go +++ b/chasm/lib/activity/responses.go @@ -10,11 +10,74 @@ import ( "go.temporal.io/api/workflowservice/v1" "go.temporal.io/server/chasm" "go.temporal.io/server/chasm/lib/activity/gen/activitypb/v1" + "go.temporal.io/server/common/tasktoken" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/durationpb" "google.golang.org/protobuf/types/known/timestamppb" ) +type eagerActivityTaskRequest struct { + namespaceID string + namespace string + requestID string +} + +func (a *Activity) buildEagerActivityTask( + ctx chasm.Context, + request eagerActivityTaskRequest, +) (*workflowservice.PollActivityTaskQueueResponse, error) { + attempt := a.LastAttempt.Get(ctx) + if !a.hasAttemptInProgress() || attempt.GetCount() != 1 || attempt.GetStartRequestId() != request.requestID { + return nil, nil + } + + componentRef, err := ctx.Ref(a) + if err != nil { + return nil, err + } + key := ctx.ExecutionKey() + token, err := tasktoken.NewSerializer().Serialize(tasktoken.NewActivityTaskToken( + request.namespaceID, + "", + key.RunID, + 0, + key.BusinessID, + a.GetActivityType().GetName(), + attempt.GetCount(), + nil, + 0, + 0, + componentRef, + attempt.GetStartedStamp(), + )) + if err != nil { + return nil, err + } + + requestData := a.RequestData.Get(ctx) + lastHeartbeat, _ := a.LastHeartbeat.TryGet(ctx) + return &workflowservice.PollActivityTaskQueueResponse{ + TaskToken: token, + WorkflowNamespace: request.namespace, + WorkflowExecution: &commonpb.WorkflowExecution{RunId: key.RunID}, + ActivityType: a.GetActivityType(), + ActivityId: key.BusinessID, + Header: requestData.GetHeader(), + Input: requestData.GetInput(), + HeartbeatDetails: lastHeartbeat.GetDetails(), + ScheduledTime: a.GetScheduleTime(), + CurrentAttemptScheduledTime: a.dispatchTimeForAttempt(attempt), + StartedTime: attempt.GetStartedTime(), + Attempt: attempt.GetCount(), + ScheduleToCloseTimeout: a.GetScheduleToCloseTimeout(), + StartToCloseTimeout: a.GetStartToCloseTimeout(), + HeartbeatTimeout: a.GetHeartbeatTimeout(), + RetryPolicy: a.GetRetryPolicy(), + Priority: a.GetPriority(), + ActivityRunId: key.RunID, + }, nil +} + // Projection of activity state onto the API response protos. // InternalStatusToAPIStatus converts internal activity execution status to API status. diff --git a/chasm/lib/activity/statemachine.go b/chasm/lib/activity/statemachine.go index a9cd9be2d07..5b5e080e818 100644 --- a/chasm/lib/activity/statemachine.go +++ b/chasm/lib/activity/statemachine.go @@ -35,6 +35,11 @@ func (a *Activity) SetStateMachineState(state activitypb.ActivityExecutionStatus } type ( + eagerStartEvent struct { + requestID string + identity string + } + rescheduleEvent struct { retryInterval time.Duration retryIntervalSource activitypb.ActivityRetryIntervalSource @@ -98,7 +103,22 @@ var TransitionScheduled = chasm.NewTransition( }, activitypb.ACTIVITY_EXECUTION_STATUS_SCHEDULED, func(a *Activity, ctx chasm.MutableContext, _ any) error { - return a.applyScheduled(ctx) + return a.applyScheduled(ctx, true) + }, +) + +// TransitionEagerStarted atomically schedules and starts the first attempt without creating a +// Matching dispatch task. The task itself is returned by StartActivityExecution. +var TransitionEagerStarted = chasm.NewTransition( + []activitypb.ActivityExecutionStatus{ + activitypb.ACTIVITY_EXECUTION_STATUS_UNSPECIFIED, + }, + activitypb.ACTIVITY_EXECUTION_STATUS_STARTED, + func(a *Activity, ctx chasm.MutableContext, event eagerStartEvent) error { + if err := a.applyScheduled(ctx, false); err != nil { + return err + } + return a.applyEagerStarted(ctx, event) }, ) @@ -346,7 +366,7 @@ var TransitionResetAttemptFailedToScheduled = chasm.NewTransition( // The methods below are transition bodies, invoked from the transitions above once the // source state has been validated. Do not call them from outside this file. -func (a *Activity) applyScheduled(ctx chasm.MutableContext) error { +func (a *Activity) applyScheduled(ctx chasm.MutableContext, dispatch bool) error { attempt := a.LastAttempt.Get(ctx) attempt.Count++ @@ -357,7 +377,8 @@ func (a *Activity) applyScheduled(ctx chasm.MutableContext) error { dispatchTime := a.firstDispatchTime() attempt.DispatchTime = timestamppb.New(dispatchTime) - if timeout := a.GetScheduleToStartTimeout().AsDuration(); timeout > 0 { + if dispatch && a.GetScheduleToStartTimeout().AsDuration() > 0 { + timeout := a.GetScheduleToStartTimeout().AsDuration() ctx.AddTask( a, chasm.TaskAttributes{ @@ -378,15 +399,43 @@ func (a *Activity) applyScheduled(ctx chasm.MutableContext) error { &activitypb.ScheduleToCloseTimeoutTask{Stamp: a.GetScheduleToCloseStamp()}) } - dispatchAttrs := chasm.TaskAttributes{} - if dispatchTime.After(a.ScheduleTime.AsTime()) { - dispatchAttrs.ScheduledTime = dispatchTime + if dispatch { + dispatchAttrs := chasm.TaskAttributes{} + if dispatchTime.After(a.ScheduleTime.AsTime()) { + dispatchAttrs.ScheduledTime = dispatchTime + } + ctx.AddTask( + a, + dispatchAttrs, + a.newActivityDispatchTask(ctx)) + } + + return nil +} + +func (a *Activity) applyEagerStarted(ctx chasm.MutableContext, event eagerStartEvent) error { + attempt := a.LastAttempt.Get(ctx) + attempt.StartedTime = timestamppb.New(ctx.Now(a)) + attempt.StartedStamp = attempt.GetStamp() + if a.FirstAttemptStartedTime == nil { + a.FirstAttemptStartedTime = attempt.GetStartedTime() } + attempt.StartRequestId = event.requestID + attempt.LastWorkerIdentity = event.identity + attempt.SdkName = ctx.RequestHeader(headers.ClientNameHeaderName) + attempt.SdkVersion = ctx.RequestHeader(headers.ClientVersionHeaderName) + startTime := attempt.GetStartedTime().AsTime() ctx.AddTask( a, - dispatchAttrs, - a.newActivityDispatchTask(ctx)) + chasm.TaskAttributes{ScheduledTime: startTime.Add(a.GetStartToCloseTimeout().AsDuration())}, + &activitypb.StartToCloseTimeoutTask{Stamp: attempt.GetStamp()}) + if heartbeatTimeout := a.GetHeartbeatTimeout().AsDuration(); heartbeatTimeout > 0 { + ctx.AddTask( + a, + chasm.TaskAttributes{ScheduledTime: startTime.Add(heartbeatTimeout)}, + &activitypb.HeartbeatTimeoutTask{Stamp: attempt.GetStamp()}) + } return nil } diff --git a/chasm/lib/activity/statemachine_test.go b/chasm/lib/activity/statemachine_test.go index 1583c7b7ff5..68698c9de4f 100644 --- a/chasm/lib/activity/statemachine_test.go +++ b/chasm/lib/activity/statemachine_test.go @@ -196,6 +196,49 @@ func TestTransitionScheduled(t *testing.T) { } } +func TestTransitionEagerStarted(t *testing.T) { + ctx := &chasm.MockMutableContext{ + MockContext: chasm.MockContext{ + HandleNow: func(chasm.Component) time.Time { return defaultTime }, + GoCtx: headers.SetVersionsForTests(context.Background(), temporal.SDKVersion, headers.ClientNameGoSDK, "", ""), + }, + } + attempt := &activitypb.ActivityAttemptState{} + activity := &Activity{ + ActivityState: &activitypb.ActivityState{ + ActivityType: &commonpb.ActivityType{Name: "test-activity-type"}, + ScheduleTime: timestamppb.New(defaultTime), + ScheduleToCloseTimeout: durationpb.New(defaultScheduleToCloseTimeout), + ScheduleToStartTimeout: durationpb.New(defaultScheduleToStartTimeout), + StartToCloseTimeout: durationpb.New(defaultStartToCloseTimeout), + HeartbeatTimeout: durationpb.New(time.Minute), + Status: activitypb.ACTIVITY_EXECUTION_STATUS_UNSPECIFIED, + TaskQueue: &taskqueuepb.TaskQueue{Name: "test-task-queue"}, + }, + LastAttempt: chasm.NewDataField(ctx, attempt), + Outcome: chasm.NewDataField(ctx, &activitypb.ActivityOutcome{}), + } + + err := TransitionEagerStarted.Apply(activity, ctx, eagerStartEvent{ + requestID: "start-request-id", + identity: "starter", + }) + require.NoError(t, err) + require.Equal(t, activitypb.ACTIVITY_EXECUTION_STATUS_STARTED, activity.GetStatus()) + require.EqualValues(t, 1, attempt.GetCount()) + require.EqualValues(t, 1, attempt.GetStamp()) + require.EqualValues(t, 1, attempt.GetStartedStamp()) + require.Equal(t, "start-request-id", attempt.GetStartRequestId()) + require.Equal(t, "starter", attempt.GetLastWorkerIdentity()) + require.Equal(t, headers.ClientNameGoSDK, attempt.GetSdkName()) + require.Equal(t, temporal.SDKVersion, attempt.GetSdkVersion()) + + require.Len(t, ctx.Tasks, 3) + require.IsType(t, &activitypb.ScheduleToCloseTimeoutTask{}, ctx.Tasks[0].Payload) + require.IsType(t, &activitypb.StartToCloseTimeoutTask{}, ctx.Tasks[1].Payload) + require.IsType(t, &activitypb.HeartbeatTimeoutTask{}, ctx.Tasks[2].Payload) +} + func TestTransitionRescheduled(t *testing.T) { testCases := []struct { name string diff --git a/cmd/tools/check-dependencies/main.go b/cmd/tools/check-dependencies/main.go index 2410ba4eb40..e10f4d45c02 100644 --- a/cmd/tools/check-dependencies/main.go +++ b/cmd/tools/check-dependencies/main.go @@ -43,6 +43,8 @@ type moduleSpec struct { // strict rule. const apiModulePath = "go.temporal.io/api" +var replacementContainsCommit = repositoryContainsCommit + var knownModules = []moduleSpec{ { modulePath: apiModulePath, @@ -325,6 +327,12 @@ func validateMainModule( version := modVersion.Version fmt.Printf("Found %s version: %s\n", mod.modulePath, version) + if replacement, ok := findReplacement(modFile, modVersion); ok { + if mod.modulePath != apiModulePath { + return fmt.Errorf("%s@%s: replacements are not allowed", mod.modulePath, version) + } + return validateTemporaryAPIReplacement(ctx, modVersion, replacement) + } if !module.IsPseudoVersion(version) { if !semver.IsValid(version) { @@ -362,6 +370,83 @@ func findRequiredModuleVersion(modFile *modfile.File, modulePath string) (module return module.Version{}, false } +func findReplacement(modFile *modfile.File, required module.Version) (module.Version, bool) { + for _, replacement := range modFile.Replace { + if replacement.Old.Path == required.Path && + (replacement.Old.Version == "" || replacement.Old.Version == required.Version) { + return replacement.New, true + } + } + return module.Version{}, false +} + +func validateTemporaryAPIReplacement( + ctx context.Context, + required module.Version, + replacement module.Version, +) error { + if !module.IsPseudoVersion(required.Version) || !module.IsPseudoVersion(replacement.Version) { + return fmt.Errorf("%s@%s: temporary replacement %s@%s must use matching pseudo-versions", + required.Path, required.Version, replacement.Path, replacement.Version) + } + + requiredRevision, err := module.PseudoVersionRev(required.Version) + if err != nil { + return fmt.Errorf("%s@%s: failed to parse pseudo-version revision: %w", required.Path, required.Version, err) + } + replacementRevision, err := module.PseudoVersionRev(replacement.Version) + if err != nil { + return fmt.Errorf("%s@%s: failed to parse temporary replacement revision: %w", replacement.Path, replacement.Version, err) + } + if requiredRevision != replacementRevision { + return fmt.Errorf("%s@%s: temporary replacement %s@%s names a different commit", + required.Path, required.Version, replacement.Path, replacement.Version) + } + + parts := strings.Split(replacement.Path, "/") + if len(parts) != 3 || parts[0] != "github.com" || parts[2] != "api-go" { + return fmt.Errorf("%s@%s: temporary replacement must point to a GitHub api-go repository, got %s", + required.Path, required.Version, replacement.Path) + } + + repoURL := "https://" + replacement.Path + ".git" + available, err := replacementContainsCommit(ctx, repoURL, replacementRevision) + if err != nil { + return fmt.Errorf("%s@%s: failed to resolve temporary replacement: %w", required.Path, required.Version, err) + } + if !available { + return fmt.Errorf("%s@%s: temporary replacement commit %s is not available from %s", + required.Path, required.Version, replacementRevision, repoURL) + } + + fmt.Printf(" - %s@%s temporarily replaced by %s@%s (ok)\n", + required.Path, required.Version, replacement.Path, replacement.Version) + return nil +} + +func repositoryContainsCommit(ctx context.Context, repoURL string, shortHash string) (bool, error) { + tmpRepo, err := os.MkdirTemp("", "check-dependencies-*") + if err != nil { + return false, fmt.Errorf("failed to create temp repo dir: %w", err) + } + defer func() { _ = os.RemoveAll(tmpRepo) }() + + out, err := exec.CommandContext(ctx, "git", "clone", "--bare", "--filter=blob:none", repoURL, tmpRepo).CombinedOutput() + if err != nil { + return false, fmt.Errorf("git clone failed: %w: %s", err, strings.TrimSpace(string(out))) + } + + out, err = exec.CommandContext(ctx, "git", "-C", tmpRepo, "cat-file", "-e", shortHash+"^{commit}").CombinedOutput() + if err == nil { + return true, nil + } + var exitErr *exec.ExitError + if errors.As(err, &exitErr) && exitErr.ExitCode() == 1 { + return false, nil + } + return false, fmt.Errorf("git cat-file failed: %w: %s", err, strings.TrimSpace(string(out))) +} + // resolveModuleOriginForSpec reports whether shortHash is reachable from the // default branch of mod's repository. // diff --git a/cmd/tools/check-dependencies/main_test.go b/cmd/tools/check-dependencies/main_test.go index 2778a2dd2d2..26189647ccd 100644 --- a/cmd/tools/check-dependencies/main_test.go +++ b/cmd/tools/check-dependencies/main_test.go @@ -48,6 +48,61 @@ func TestFindRequiredModuleVersion(t *testing.T) { }) } +func TestFindReplacement(t *testing.T) { + f := parseGoMod(t, `module test + +go 1.21 + +require go.temporal.io/api v1.2.4-0.20240101000000-abcdef012345 + +replace go.temporal.io/api => github.com/example/api-go v1.2.4-0.20240101000000-abcdef012345 +`) + + replacement, ok := findReplacement(f, module.Version{ + Path: "go.temporal.io/api", + Version: "v1.2.4-0.20240101000000-abcdef012345", + }) + require.True(t, ok) + require.Equal(t, "github.com/example/api-go", replacement.Path) + require.Equal(t, "v1.2.4-0.20240101000000-abcdef012345", replacement.Version) +} + +func TestValidateTemporaryAPIReplacement(t *testing.T) { + const version = "v1.2.4-0.20240101000000-abcdef012345" + originalResolver := replacementContainsCommit + t.Cleanup(func() { replacementContainsCommit = originalResolver }) + + var gotRepoURL string + var gotRevision string + replacementContainsCommit = func(_ context.Context, repoURL, revision string) (bool, error) { + gotRepoURL = repoURL + gotRevision = revision + return true, nil + } + + required := module.Version{Path: apiModulePath, Version: version} + replacement := module.Version{Path: "github.com/example/api-go", Version: version} + require.NoError(t, validateTemporaryAPIReplacement(context.Background(), required, replacement)) + require.Equal(t, "https://github.com/example/api-go.git", gotRepoURL) + require.Equal(t, "abcdef012345", gotRevision) + + t.Run("rejects a different revision", func(t *testing.T) { + err := validateTemporaryAPIReplacement(context.Background(), required, module.Version{ + Path: replacement.Path, + Version: "v1.2.4-0.20240101000000-fedcba987654", + }) + require.ErrorContains(t, err, "names a different commit") + }) + + t.Run("rejects a non api-go repository", func(t *testing.T) { + err := validateTemporaryAPIReplacement(context.Background(), required, module.Version{ + Path: "github.com/example/not-api-go", + Version: version, + }) + require.ErrorContains(t, err, "must point to a GitHub api-go repository") + }) +} + func TestValidateReleaseBranch(t *testing.T) { tests := []struct { name string diff --git a/common/metrics/metric_defs.go b/common/metrics/metric_defs.go index c425bf729c5..7bb948f0e45 100644 --- a/common/metrics/metric_defs.go +++ b/common/metrics/metric_defs.go @@ -1024,7 +1024,9 @@ var ( SpeculativeWorkflowTaskCommits = NewCounterDef("speculative_workflow_task_commits") SpeculativeWorkflowTaskRollbacks = NewCounterDef("speculative_workflow_task_rollbacks") - ActivityEagerExecutionCounter = NewCounterDef("activity_eager_execution") + ActivityEagerExecutionCounter = NewCounterDef("activity_eager_execution") + StandaloneActivityEagerStartAcceptedCounter = NewCounterDef("standalone_activity_eager_start_accepted") + StandaloneActivityEagerStartDeniedCounter = NewCounterDef("standalone_activity_eager_start_denied") // WorkflowEagerExecutionCounter is emitted any time eager workflow start is requested. WorkflowEagerExecutionCounter = NewCounterDef("workflow_eager_execution") // WorkflowEagerExecutionDeniedCounter is emitted any time eager workflow start is requested and the serer fell back diff --git a/go.mod b/go.mod index 26dc89de4c7..299d7067cb6 100644 --- a/go.mod +++ b/go.mod @@ -70,7 +70,7 @@ require ( go.opentelemetry.io/otel/sdk/metric v1.44.0 go.opentelemetry.io/otel/trace v1.44.0 go.opentelemetry.io/proto/otlp v1.10.0 - go.temporal.io/api v1.63.6-0.20260909222256-20151aa90480 + go.temporal.io/api v1.63.7-0.20261002150604-6b55806007ed go.temporal.io/auto-scaled-workers v0.0.0-20260928183627-719679297cbe go.temporal.io/sdk v1.48.0 go.uber.org/fx v1.24.0 @@ -223,4 +223,6 @@ require ( sigs.k8s.io/yaml v1.6.0 // indirect ) +replace go.temporal.io/api => github.com/wenlong-gu/api-go v1.63.7-0.20261002150604-6b55806007ed + tool golang.org/x/perf/cmd/benchstat diff --git a/go.sum b/go.sum index 7c71b418ed4..dc276bb794a 100644 --- a/go.sum +++ b/go.sum @@ -425,6 +425,8 @@ github.com/urfave/cli v1.22.17 h1:SYzXoiPfQjHBbkYxbew5prZHS1TOLT3ierW8SYLqtVQ= github.com/urfave/cli v1.22.17/go.mod h1:b0ht0aqgH/6pBYzzxURyrM4xXNgsoT/n2ZzwQiEhNVo= github.com/urfave/cli/v2 v2.27.7 h1:bH59vdhbjLv3LAvIu6gd0usJHgoTTPhCFib8qqOwXYU= github.com/urfave/cli/v2 v2.27.7/go.mod h1:CyNAG/xg+iAOg0N4MPGZqVmv2rCoP267496AOXUZjA4= +github.com/wenlong-gu/api-go v1.63.7-0.20261002150604-6b55806007ed h1:Jr0wN0pOdf8USIjmOL/v7t8P1dH1oBnnrMsZhaM3klg= +github.com/wenlong-gu/api-go v1.63.7-0.20261002150604-6b55806007ed/go.mod h1:acM0I9WPuYg8W3Pd9jOZvEgi7mRUttUQ4+e7fowKVnM= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342 h1:FnBeRrxr7OU4VvAzt5X7s6266i6cSVkkFPS0TuXWbIg= @@ -479,8 +481,6 @@ go.opentelemetry.io/proto/slim/otlp/collector/profiles/v1development v0.3.0 h1:R go.opentelemetry.io/proto/slim/otlp/collector/profiles/v1development v0.3.0/go.mod h1:I89cynRj8y+383o7tEQVg2SVA6SRgDVIouWPUVXjx0U= go.opentelemetry.io/proto/slim/otlp/profiles/v1development v0.3.0 h1:CQvJSldHRUN6Z8jsUeYv8J0lXRvygALXIzsmAeCcZE0= go.opentelemetry.io/proto/slim/otlp/profiles/v1development v0.3.0/go.mod h1:xSQ+mEfJe/GjK1LXEyVOoSI1N9JV9ZI923X5kup43W4= -go.temporal.io/api v1.63.6-0.20260909222256-20151aa90480 h1:1w2xEZFpL7R9NagEQeWTh7oZmZU3YUJGogusn9/LfCg= -go.temporal.io/api v1.63.6-0.20260909222256-20151aa90480/go.mod h1:acM0I9WPuYg8W3Pd9jOZvEgi7mRUttUQ4+e7fowKVnM= go.temporal.io/auto-scaled-workers v0.0.0-20260928183627-719679297cbe h1:BlshPqVI1uh/AsdfGPPjfKc37HEZdGfcmvx+uW+rbe8= go.temporal.io/auto-scaled-workers v0.0.0-20260928183627-719679297cbe/go.mod h1:6Q4guHwlbJldBOZwvc201E0+Jz9Tly1L1Db6C7RJuLw= go.temporal.io/sdk v1.48.0 h1:WDctKDVuh0Z8Nf7euAyqs/EwcPg1JTIIq1Fut8Tq118= diff --git a/tests/activity_driver.go b/tests/activity_driver.go index 360c42f23dc..a563bcd30ee 100644 --- a/tests/activity_driver.go +++ b/tests/activity_driver.go @@ -41,6 +41,7 @@ import ( // always send start-to-close, defaulted long enough not to fire. The other timeouts are simply // absent when unset. type activityConfig struct { + EagerStart bool MaxAttempts int32 // RetryPolicy MaximumAttempts; 0 = unlimited RetryInterval time.Duration // RetryPolicy InitialInterval; 0 => activityShortRetryInterval BackoffCoefficient float64 // RetryPolicy BackoffCoefficient; 0 => 1.0 (constant interval) @@ -157,12 +158,13 @@ type activityTerminalOutcome struct { // ways the options alone settle that no retry can follow. Deriving it means the two cannot disagree. func (c activityConfig) modelConfig() model.Config { return model.Config{ - MaxAttempts: c.MaxAttempts, - HasStartDelay: c.StartDelay > 0, - HasScheduleToClose: c.ScheduleToClose > 0, - HasScheduleToStart: c.ScheduleToStart > 0, - HasHeartbeat: c.HeartbeatTimeout > 0, - NonRetryableTimeouts: c.nonRetryableTimeouts(), + InitialAttemptStarted: c.EagerStart, + MaxAttempts: c.MaxAttempts, + HasStartDelay: c.StartDelay > 0, + HasScheduleToClose: c.ScheduleToClose > 0, + HasScheduleToStart: c.ScheduleToStart > 0, + HasHeartbeat: c.HeartbeatTimeout > 0, + NonRetryableTimeouts: c.nonRetryableTimeouts(), // In reality, the server decides this based on how long the attempt took, and any // NextRetryDelay sent by the worker. The model cannot express that. Instead it supports a // fixed retry delay and the calculation here pretends that the attempt duration is zero. diff --git a/tests/activity_parity_test.go b/tests/activity_parity_test.go index bae153f3ec8..9218ac2f11d 100644 --- a/tests/activity_parity_test.go +++ b/tests/activity_parity_test.go @@ -41,10 +41,38 @@ func newActivityParityEnv(t *testing.T) *testcore.TestEnv { cluster := env.GetTestCluster() cluster.OverrideDynamicConfig(t, dynamicconfig.EnableChasm, nsValues(true)) cluster.OverrideDynamicConfig(t, activity.Enabled, nsValues(true)) + cluster.OverrideDynamicConfig(t, dynamicconfig.EnableActivityEagerExecution, nsValues(true)) + cluster.OverrideDynamicConfig(t, activity.EnableEagerStart, nsValues(true)) cluster.OverrideDynamicConfig(t, activity.EnableStandaloneActivityOperatorCommands, nsValues(true)) return env } +func (s *activityParityTestSuite) TestEagerActivityStartParity() { + env := newActivityParityEnv(s.T()) + cfg := activityConfig{ + EagerStart: true, + MaxAttempts: 2, + RetryInterval: activityShortDispatchDelay, + } + + // Both implementations return the first task already started. A retry of that task must then + // return to the ordinary Matching dispatch path before its second attempt starts. + trace := []model.Event{ + model.Heartbeat, + model.FailRetryably, + model.BackoffElapses, + model.Poll, + model.Complete, + } + + s.Run("WorkflowActivity", func(s *activityParityTestSuite) { + newWFADriver(s.T(), env, cfg).driveTrace(s.T(), trace) + }) + s.Run("StandaloneActivity", func(s *activityParityTestSuite) { + newSAADriver(s.T(), env, cfg).driveTrace(s.T(), trace) + }) +} + func assertActivityTaskNotCancelRequested(t *testing.T, err error) { var invalidArgumentErr *serviceerror.InvalidArgument require.ErrorAs(t, err, &invalidArgumentErr) diff --git a/tests/activity_standalone_driver.go b/tests/activity_standalone_driver.go index d9ee39457a7..6807b4ffa0f 100644 --- a/tests/activity_standalone_driver.go +++ b/tests/activity_standalone_driver.go @@ -118,8 +118,15 @@ func (d *saaDriver) start(t require.TestingT, cfg activityConfig) *saaHandle { id := fmt.Sprintf("%s-%d", d.activityIDPrefix, d.numStarted) resp, err := d.env.FrontendClient().StartActivityExecution(d.testContext(), d.startRequest(cfg, id, id)) require.NoError(t, err) + state := activityDriverState{cfg: cfg} + if cfg.EagerStart { + task := resp.GetEagerActivityTask() + require.NotNil(t, task, "eager standalone activity start did not return a task") + state.token = task.GetTaskToken() + state.startedAttempt = task.GetAttempt() + } return &saaHandle{ - activityDriverState: activityDriverState{cfg: cfg}, + activityDriverState: state, model: newActivityModel(cfg), d: d, activityID: id, @@ -137,6 +144,7 @@ func (d *saaDriver) startRequest(c activityConfig, activityID, taskQueue string) } return &workflowservice.StartActivityExecutionRequest{ Namespace: d.env.Namespace().String(), + RequestEagerExecution: c.EagerStart, ActivityId: activityID, ActivityType: d.env.Tv().ActivityType(), Identity: d.env.Tv().ClientIdentity(), diff --git a/tests/activity_standalone_test.go b/tests/activity_standalone_test.go index ded66e633ac..4e5827a8874 100644 --- a/tests/activity_standalone_test.go +++ b/tests/activity_standalone_test.go @@ -1207,6 +1207,63 @@ func (s *standaloneActivityTestSuite) TestStart() { }) } +func (s *standaloneActivityTestSuite) TestEagerStartResponseAndNoRedelivery() { + env := s.newTestEnv() + t := s.T() + ctx := metadata.NewOutgoingContext(s.Context(), metadata.Pairs( + headers.ClientNameHeaderName, headers.ClientNameGoSDK, + headers.ClientVersionHeaderName, temporal.SDKVersion, + )) + activityID := testcore.RandomizeStr(t.Name()) + taskQueue := testcore.RandomizeStr(t.Name()) + requestID := testcore.RandomizeStr(t.Name()) + request := &workflowservice.StartActivityExecutionRequest{ + Namespace: env.Namespace().String(), + RequestEagerExecution: true, + ActivityId: activityID, + ActivityType: env.Tv().ActivityType(), + Identity: defaultIdentity, + Input: defaultInput, + TaskQueue: &taskqueuepb.TaskQueue{Name: taskQueue}, + StartToCloseTimeout: durationpb.New(defaultStartToCloseTimeout), + RequestId: requestID, + } + + first, err := env.FrontendClient().StartActivityExecution(ctx, request) + require.NoError(t, err) + require.True(t, first.GetStarted()) + require.NotNil(t, first.GetEagerActivityTask()) + require.Equal(t, activityID, first.GetEagerActivityTask().GetActivityId()) + require.Equal(t, first.GetRunId(), first.GetEagerActivityTask().GetActivityRunId()) + require.NotNil(t, first.GetEagerActivityTask().GetWorkflowExecution()) + require.Empty(t, first.GetEagerActivityTask().GetWorkflowExecution().GetWorkflowId()) + require.Equal(t, first.GetRunId(), first.GetEagerActivityTask().GetWorkflowExecution().GetRunId()) + require.EqualValues(t, 1, first.GetEagerActivityTask().GetAttempt()) + + describe, err := env.FrontendClient().DescribeActivityExecution(ctx, &workflowservice.DescribeActivityExecutionRequest{ + Namespace: env.Namespace().String(), + ActivityId: activityID, + RunId: first.GetRunId(), + }) + require.NoError(t, err) + require.Equal(t, headers.ClientNameGoSDK, describe.GetInfo().GetSdkName()) + require.Equal(t, temporal.SDKVersion, describe.GetInfo().GetSdkVersion()) + + retry, err := env.FrontendClient().StartActivityExecution(ctx, request) + require.NoError(t, err) + require.False(t, retry.GetStarted()) + require.Nil(t, retry.GetEagerActivityTask()) + + useExisting := common.CloneProto(request) + useExisting.RequestId = testcore.RandomizeStr(t.Name()) + useExisting.IdConflictPolicy = enumspb.ACTIVITY_ID_CONFLICT_POLICY_USE_EXISTING + conflict, err := env.FrontendClient().StartActivityExecution(ctx, useExisting) + require.NoError(t, err) + require.False(t, conflict.GetStarted()) + require.Nil(t, conflict.GetEagerActivityTask()) + +} + func (s *standaloneActivityTestSuite) TestComplete() { env := s.newTestEnv() t := s.T() diff --git a/tests/activity_workflow_driver.go b/tests/activity_workflow_driver.go index 73917e5a2cd..0e088b6f8e2 100644 --- a/tests/activity_workflow_driver.go +++ b/tests/activity_workflow_driver.go @@ -16,6 +16,8 @@ import ( "github.com/google/uuid" "github.com/stretchr/testify/require" activitypb "go.temporal.io/api/activity/v1" + commandpb "go.temporal.io/api/command/v1" + commonpb "go.temporal.io/api/common/v1" enumspb "go.temporal.io/api/enums/v1" taskqueuepb "go.temporal.io/api/taskqueue/v1" workflowpb "go.temporal.io/api/workflow/v1" @@ -122,6 +124,9 @@ func (a *wfaHandle) awaitDispatchDelay(t testing.TB, e model.Event) { } func (d *wfaDriver) start(t *testing.T, cfg activityConfig) *wfaHandle { + if cfg.EagerStart { + return d.startEager(t, cfg) + } cfg.StartDelay = 0 // WFA does not support start delay, but SAA/WFA tests often share config wfTQ := testcore.RandomizeStr("wfa-wf") actTQ := testcore.RandomizeStr("wfa-act") @@ -158,6 +163,88 @@ func (d *wfaDriver) start(t *testing.T, cfg activityConfig) *wfaHandle { return a } +func (d *wfaDriver) startEager(t *testing.T, cfg activityConfig) *wfaHandle { + wfTQ := testcore.RandomizeStr("wfa-eager-wf") + actTQ := testcore.RandomizeStr("wfa-eager-act") + const actID = "act" + wfID := testcore.RandomizeStr("wfa-eager-run") + fc := d.env.FrontendClient() + + started, err := fc.StartWorkflowExecution(d.testContext(), &workflowservice.StartWorkflowExecutionRequest{ + Namespace: d.env.Namespace().String(), + WorkflowId: wfID, + WorkflowType: &commonpb.WorkflowType{Name: "eagerActivityDriver"}, + TaskQueue: &taskqueuepb.TaskQueue{Name: wfTQ}, + RequestId: uuid.NewString(), + }) + require.NoError(t, err) + + ctx, cancel := context.WithTimeout(d.testContext(), activityDriverTimeout) + defer cancel() + workflowTask, err := fc.PollWorkflowTaskQueue(ctx, &workflowservice.PollWorkflowTaskQueueRequest{ + Namespace: d.env.Namespace().String(), + TaskQueue: &taskqueuepb.TaskQueue{Name: wfTQ}, + Identity: d.env.Tv().WorkerIdentity(), + }) + require.NoError(t, err) + require.NotEmpty(t, workflowTask.GetTaskToken(), "workflow start did not produce a workflow task") + + activityTask, err := fc.RespondWorkflowTaskCompleted(d.testContext(), &workflowservice.RespondWorkflowTaskCompletedRequest{ + Namespace: d.env.Namespace().String(), + Identity: d.env.Tv().WorkerIdentity(), + TaskToken: workflowTask.GetTaskToken(), + Commands: []*commandpb.Command{{ + CommandType: enumspb.COMMAND_TYPE_SCHEDULE_ACTIVITY_TASK, + Attributes: &commandpb.Command_ScheduleActivityTaskCommandAttributes{ + ScheduleActivityTaskCommandAttributes: &commandpb.ScheduleActivityTaskCommandAttributes{ + ActivityId: actID, + ActivityType: d.env.Tv().ActivityType(), + TaskQueue: &taskqueuepb.TaskQueue{Name: actTQ}, + Input: payloads.EncodeString(activityInput), + StartToCloseTimeout: durationpb.New(cfg.startToClose()), + ScheduleToCloseTimeout: optionalDuration(cfg.ScheduleToClose), + ScheduleToStartTimeout: optionalDuration(cfg.ScheduleToStart), + HeartbeatTimeout: optionalDuration(cfg.HeartbeatTimeout), + RetryPolicy: &commonpb.RetryPolicy{ + InitialInterval: durationpb.New(cfg.retryInterval()), + BackoffCoefficient: cmp.Or(cfg.BackoffCoefficient, 1.0), + MaximumInterval: durationpb.New(cmp.Or(cfg.MaxRetryInterval, cfg.retryInterval())), + MaximumAttempts: cfg.MaxAttempts, + NonRetryableErrorTypes: cfg.NonRetryableErrorTypes, + }, + RequestEagerExecution: true, + }, + }, + }}, + }) + require.NoError(t, err) + require.Len(t, activityTask.GetActivityTasks(), 1, "eager workflow activity start did not return a task") + task := activityTask.GetActivityTasks()[0] + + t.Cleanup(func() { + if err := d.env.SdkClient().TerminateWorkflow(context.Background(), wfID, started.GetRunId(), "eager activity driver cleanup"); err != nil { + t.Logf("eager activity driver cleanup: %v", err) + } + }) + return &wfaHandle{ + activityDriverState: activityDriverState{cfg: cfg, token: task.GetTaskToken(), startedAttempt: task.GetAttempt()}, + model: newActivityModel(cfg), + d: d, + run: d.env.SdkClient().GetWorkflow(d.testContext(), wfID, started.GetRunId()), + workflowID: wfID, + runID: started.GetRunId(), + activityID: actID, + taskQueue: actTQ, + } +} + +func optionalDuration(value time.Duration) *durationpb.Duration { + if value == 0 { + return nil + } + return durationpb.New(value) +} + // wfaActivityParams is what the helper workflow needs to schedule the activity: the activity the // test described, and where to put it. type wfaActivityParams struct { diff --git a/tests/mixedbrain/go.mod b/tests/mixedbrain/go.mod index 533f6e6183f..4e44d0a6674 100644 --- a/tests/mixedbrain/go.mod +++ b/tests/mixedbrain/go.mod @@ -7,7 +7,7 @@ require ( github.com/siderolabs/grpc-proxy v0.5.2 github.com/stretchr/testify v1.11.1 github.com/temporalio/omes v0.0.0-20260915193711-9667582a3051 - go.temporal.io/api v1.63.6-0.20260909222256-20151aa90480 + go.temporal.io/api v1.63.7-0.20261002150604-6b55806007ed go.temporal.io/server v0.0.0-00010101000000-000000000000 google.golang.org/grpc v1.83.2 google.golang.org/protobuf v1.36.11 @@ -31,6 +31,8 @@ require ( gopkg.in/yaml.v3 v3.0.1 // indirect ) +replace go.temporal.io/api => github.com/wenlong-gu/api-go v1.63.7-0.20261002150604-6b55806007ed + replace go.temporal.io/server => ../.. replace go.temporal.io/sdk => go.temporal.io/sdk v1.42.0 diff --git a/tests/mixedbrain/go.sum b/tests/mixedbrain/go.sum index b2e0bb45bdb..881af88d9ed 100644 --- a/tests/mixedbrain/go.sum +++ b/tests/mixedbrain/go.sum @@ -1,230 +1,54 @@ -cel.dev/expr v0.25.2/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4= -cloud.google.com/go v0.123.0/go.mod h1:xBoMV08QcqUGuPW65Qfm1o9Y4zKZBpGS+7bImXLTAZU= -cloud.google.com/go/auth v0.20.0/go.mod h1:942/yi/itH1SsmpyrbnTMDgGfdy2BUqIKyd0cyYLc5Q= -cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= -cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= -cloud.google.com/go/iam v1.9.0/go.mod h1:KP+nKGugNJW4LcLx1uEZcq1ok5sQHFaQehQNl4QDgV4= -cloud.google.com/go/longrunning v0.11.0/go.mod h1:8nqFBPOO1U/XkhWl0I19AMZEphrHi73VNABIpKYaTwM= -cloud.google.com/go/monitoring v1.27.0/go.mod h1:72NOVjJXHY/HBfoLT0+qlCZBT059+9VXLeAnL2PeeVM= -cloud.google.com/go/run v1.19.0/go.mod h1:Z5wHbyFirI8XU48EPs5XJf/qmVm1SXZEhuS8EvZOuQU= -cloud.google.com/go/storage v1.62.1/go.mod h1:cpYz/kRVZ+UQAF1uHeea10/9ewcRbxGoGNKsS9daSXA= -dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= -filippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.33.0/go.mod h1:pJTkW8hEUIIi3Pf65lPZOnn4Y81yCllX6IWk2jNXdkM= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.56.0/go.mod h1:hEpiGU18xf70qb3jbTcIggWAiEfX/cOIVc2OTe4OegA= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.56.0/go.mod h1:6ZZMQhZKDvUvkJw2rc+oDP90tMMzuU/J+5HG1ZmPOmE= -github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= -github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= -github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0= -github.com/aclements/go-moremath v0.0.0-20210112150236-f10218a38794/go.mod h1:7e+I0LQFUI9AXWxOfsQROs9xPhoJtbsyWcjJqDd4KPY= -github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/apache/thrift v0.24.0/go.mod h1:zPt6WxgvTOM6hF92y8C+MkEM5LMxZuk4JcQOiU4Esvs= -github.com/aws/aws-sdk-go-v2 v1.41.6/go.mod h1:dy0UzBIfwSeot4grGvY1AqFWN5zgziMmWGzysDnHFcQ= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.9/go.mod h1:uOYhgfgThm/ZyAuJGNQ5YgNyOlYfqnGpTHXvk3cpykg= -github.com/aws/aws-sdk-go-v2/config v1.32.16/go.mod h1:duCCnJEFqpt2RC6no1iK6q+8HpwOAkiUua0pY507dQc= -github.com/aws/aws-sdk-go-v2/credentials v1.19.15/go.mod h1:gJiYyMOjNg8OEdRWOf3CrFQxM2a98qmrtjx1zuiQfB8= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.22/go.mod h1:b+hYdbU+jGKfXE8kKM6g1+h+L/Go3vMvzlxBsiuGsxg= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.22/go.mod h1:6sW9iWm9DK9YRpRGga/qzrzNLgKpT2cIxb7Vo2eNOp0= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.22/go.mod h1:KIpEUx0JuRZLO7U6cbV204cWAEco2iC3l061IxlwLtI= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.23/go.mod h1:7J8iGMdRKk6lw2C+cMIphgAnT8uTwBwNOsGkyOCm80U= -github.com/aws/aws-sdk-go-v2/service/ecs v1.78.1/go.mod h1:1DlTqkp+8uc5At3UXyJAvJXFaWoMmxSHcp2Zdor0qGw= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.8/go.mod h1:VsK9abqQeGlzPgUr+isNWzPlK2vKe9INMLWnY65f5Xs= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.14/go.mod h1:yS5rNogD8e0Wu9+l3MUwr6eENBzEeGejvINpN5PAYfY= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.22/go.mod h1:nO6egFBoAaoXze24a2C0NjQCvdpk8OueRoYimvEB9jo= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.22/go.mod h1:ES3ynECd7fYeJIL6+oax+uIEljmfps0S70BaQzbMd/o= -github.com/aws/aws-sdk-go-v2/service/lambda v1.89.1/go.mod h1:7qoh/MlWG5QCnZwq9bvdXomEAkmumayXcjEjIemIV7U= -github.com/aws/aws-sdk-go-v2/service/s3 v1.99.1/go.mod h1:Fw9aqhJicIVee1VytBBjH+l+5ov6/PhbtIK/u3rt/ls= -github.com/aws/aws-sdk-go-v2/service/signin v1.0.10/go.mod h1:p6+MXNxW7IA6dMgHfTAzljuwSKD0NCm/4lbS4t6+7vI= -github.com/aws/aws-sdk-go-v2/service/sso v1.30.16/go.mod h1:CudnEVKRtLn0+3uMV0yEXZ+YZOKnAtUJ5DmDhilVnIw= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.20/go.mod h1:JHs8/y1f3zY7U5WcuzoJ/yAYGYtNIVPKLIbp61euvmg= -github.com/aws/aws-sdk-go-v2/service/sts v1.42.0/go.mod h1:pFw33T0WLvXU3rw1WBkpMlkgIn54eCB5FYLhjDc9Foo= -github.com/aws/smithy-go v1.25.0/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= -github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= -github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= -github.com/cactus/go-statsd-client/statsd v0.0.0-20200423205355-cb0885a1018c/go.mod h1:l/bIBLeOl9eX+wxJAzxS4TveKRtAqlyDpHjhkfO0MEI= -github.com/cactus/go-statsd-client/v5 v5.1.0/go.mod h1:COEvJ1E+/E2L4q6QE5CkjWPi4eeDw9maJBMIuMPBZbY= -github.com/caio/go-tdigest/v5 v5.0.0/go.mod h1:wI618wZoAYzIDZlpX2CfyTQdrdGtwEZOJuXdrI3zk/Y= -github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM= -github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2/go.mod h1:qwXFYgsP6T7XnJtbKlf1HP8AjxZZyzxMmc+Lq5GjlU4= -github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= -github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= -github.com/ebitengine/purego v0.9.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= -github.com/emicklei/go-restful/v3 v3.13.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= -github.com/envoyproxy/go-control-plane v0.14.0/go.mod h1:NcS5X47pLl/hfqxU70yPwL9ZMkUlwlKxtAohpi2wBEU= -github.com/envoyproxy/go-control-plane/envoy v1.37.0/go.mod h1:DReE9MMrmecPy+YvQOAOHNYMALuowAnbjjEMkkWOi6A= -github.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJPzVVHnPgRKdUdwW/KdbRt94AzgRee4= -github.com/envoyproxy/protoc-gen-validate v1.3.3/go.mod h1:TsndJ/ngyIdQRhMcVVGDDHINPLWB7C82oDArY51KfB0= -github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a/go.mod h1:7Ga40egUymuWXxAe151lTNnCv97MddSOVsjpPPkityA= -github.com/fatih/color v1.19.0/go.mod h1:zNk67I0ZUT1bEGsSGyCZYZNrHuTkJJB+r6Q9VuMi0LE= -github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fullstorydev/grpchan v1.1.1 h1:heQqIJlAv5Cnks9a70GRL2EJke6QQoUB25VGR6TZQas= github.com/fullstorydev/grpchan v1.1.1/go.mod h1:f4HpiV8V6htfY/K44GWV1ESQzHBTq7DinhzqQ95lpgc= -github.com/fxamacker/cbor/v2 v2.9.1/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= -github.com/go-faker/faker/v4 v4.7.0/go.mod h1:u1dIRP5neLB6kTzgyVjdBOV5R1uP7BdxkcWk7tiKQXk= -github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= -github.com/go-openapi/jsonpointer v0.23.1/go.mod h1:iWRmZTrGn7XwYhtPt/fvdSFj1OfNBngqRT2UG3BxSqY= -github.com/go-openapi/jsonreference v0.21.5/go.mod h1:u25Bw85sX4E2jzFodh1FOKMTZLcfifd1Q+iKKOUxExw= -github.com/go-openapi/swag v0.26.0/go.mod h1:82g3193sZJRbocs7bNCqGfIgq8pkuwVwCfhKIRlEQF0= -github.com/go-openapi/swag/cmdutils v0.26.0/go.mod h1:Sm1MVFMkF6guJJ+pQqHnQA3N0j9qALV3NxzDSv6bETM= -github.com/go-openapi/swag/conv v0.26.0/go.mod h1:tpAmIL7X58VPnHHiSO4uE3jBeRamGsFsfdDeDtb5ECE= -github.com/go-openapi/swag/fileutils v0.26.0/go.mod h1:0WDJ7lp67eNjPMO50wAWYlKvhOb6CQ37rzR7wrgI8Tc= -github.com/go-openapi/swag/jsonname v0.26.0/go.mod h1:urBBR8bZNoDYGr653ynhIx+gTeIz0ARZxHkAPktJK2M= -github.com/go-openapi/swag/jsonutils v0.26.0/go.mod h1:2VmA0CJlyFqgawOaPI9psnjFDqzyivIqLYN34t9p91E= -github.com/go-openapi/swag/loading v0.26.0/go.mod h1:dBxQ/6V2uBaAQdevN18VELE6xSpJWZxLX4txe12JwDg= -github.com/go-openapi/swag/mangling v0.26.0/go.mod h1:jifS7W9vbg+pw63bT+GI53otluMQL3CeemuyCHKwVx0= -github.com/go-openapi/swag/netutils v0.26.0/go.mod h1:5iK+Ok3ZohWWex1C50BFTPexi03UaPwjW4Oj8kgrpwo= -github.com/go-openapi/swag/stringutils v0.26.0/go.mod h1:sWn5uY+QIIspwPhvgnqJsH8xqFT2ZbYcvbcFanRyhFE= -github.com/go-openapi/swag/typeutils v0.26.0/go.mod h1:oovDuIUvTrEHVMqWilQzKzV4YlSKgyZmFh7AlfABNVE= -github.com/go-openapi/swag/yamlutils v0.26.0/go.mod h1:1evKEGAtP37Pkwcc7EWMF0hedX0/x3Rkvei2wtG/TbU= -github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU= -github.com/gocql/gocql v1.7.0/go.mod h1:vnlvXyFZeLBF0Wy+RS8hrOdbn0UWsWtdg07XJnFxZ+4= github.com/gofrs/flock v0.13.0 h1:95JolYOvGMqeH31+FC7D2+uULf6mG61mEZ/A8dRYMzw= github.com/gofrs/flock v0.13.0/go.mod h1:jxeyy9R1auM5S6JYDBhDt+E2TCo7DkratH4Pgi8P+Z0= -github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -github.com/golang/glog v1.2.5/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= -github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/gnostic-models v0.7.1/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.3.15/go.mod h1:vqVt9yG9480NtzREnTlmGSBmFrA+bzb0yl0TxoBQXOg= -github.com/googleapis/gax-go/v2 v2.22.0/go.mod h1:irWBbALSr0Sk3qlqb9SyJ1h68WjgeFuiOzI4Rqw5+aY= -github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= -github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3/go.mod h1:NbCUVmiS4foBGBHOYlCT25+YmGpJ32dZPi75pGEUpj4= github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 h1:5VipnvEpbqr2gA2VbM+nYVbkIF28c5ZQfqCBQ5g2xfk= github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0/go.mod h1:Hyl3n6Twe1hvtd9XUXDec4pTvgMSEixRuQKPTMH2bNs= -github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/go-version v1.9.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= -github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= -github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= -github.com/jackc/pgx/v5 v5.10.0/go.mod h1:mal1tBGAFfLHvZzaYh77YS/eC6IX9OWbRV1QIIM0Jn4= -github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jhump/grpctunnel v0.3.0 h1:itddWDKl7J4CeW4nzY3S/a1s7mPZUb8UtUzEhc/R8mg= github.com/jhump/grpctunnel v0.3.0/go.mod h1:dn5zls1F+1ftPMkbh4kVTVgGuY5t/v3ZgdjtnSMC3f4= -github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY= -github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/jstemmer/go-junit-report/v2 v2.1.0/go.mod h1:mgHVr7VUo5Tn8OLVr1cKnLuEy0M92wdRntM99h7RkgQ= -github.com/klauspost/compress v1.18.5/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/lib/pq v1.12.3/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA= -github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= -github.com/mailru/easyjson v0.9.2/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= -github.com/maruel/panicparse/v2 v2.5.0/go.mod h1:DA2fDiBk63bKfBf4CVZP9gb4fuvzdPbLDsSI873hweQ= -github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= -github.com/mattn/go-isatty v0.0.21/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4= -github.com/mattn/go-runewidth v0.0.23/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= -github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= -github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= github.com/nexus-rpc/nexus-proto-annotations v0.1.0 h1:2fELd+9sqUtNu6Fg//pw8YFsxOvp8vZ8hfP0nHhNI80= github.com/nexus-rpc/nexus-proto-annotations v0.1.0/go.mod h1:n3UjF1bPCW8llR8tHvbxJ+27yPWrhpo8w/Yg1IOuY0Y= -github.com/nexus-rpc/sdk-go v0.7.0/go.mod h1:FHdPfVQwRuJFZFTF0Y2GOAxCrbIBNrcPna9slkGKPYk= -github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= -github.com/olivere/elastic/v7 v7.0.32/go.mod h1:c7PVmLe3Fxq77PIfY/bZmxY/TAamBhCzZ8xDOE09a9k= -github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= -github.com/parquet-go/parquet-go v0.25.1/go.mod h1:AXBuotO1XiBtcqJb/FKFyjBG4aqa3aQAAWF3ZPzCanY= -github.com/pelletier/go-toml/v2 v2.4.3/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= -github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= -github.com/prometheus/client_golang v1.21.0/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg= -github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= -github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= -github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo= -github.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= -github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k= -github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= -github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/shirou/gopsutil/v4 v4.25.10/go.mod h1:+kSwyC8DRUD9XXEHCAFjK+0nuArFJM0lva+StQAcskM= -github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= github.com/siderolabs/grpc-proxy v0.5.2 h1:o34tS02IxbX3qeXe0MM99zE2XhfWHuvdBtSWWRD9HNI= github.com/siderolabs/grpc-proxy v0.5.2/go.mod h1:ygf+gqFxWdymAXuP4qMHTa+j6SvasdcFg3XkhpvUvDw= -github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= -github.com/sony/gobreaker v1.0.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= -github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= -github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= -github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spiffe/go-spiffe/v2 v2.7.0/go.mod h1:47Q0Q9/AqGha8QLHp+kxpH4Wca7X7EnOtlIJy3mxZ3U= -github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -github.com/temporalio/features v0.0.0-20260908141410-6f64678c5b39/go.mod h1:Lo/kKS4Xz8QhK1DFa3B/lmaBiQCeFbGdob4i8ewuZQE= github.com/temporalio/omes v0.0.0-20260915193711-9667582a3051 h1:lJ/bCaq7PVGpSwON+MgqAK8/0oih71Jrq9OP3Gxajns= github.com/temporalio/omes v0.0.0-20260915193711-9667582a3051/go.mod h1:ECa3gdOOjrNXSR1OXjI3s+iO1DhoxDIqcyS4fxK2j0Y= -github.com/temporalio/omes/workers/go/harness/api v0.0.0-20260601200529-2419bd37e739/go.mod h1:Aa80OftxTrGB+4Vsi4MRbLA117rnrqfqC6KWVeGvFQg= -github.com/temporalio/ringpop-go v0.1.0/go.mod h1:RE+CHmY+kOZQk47AQaVzwrGmxpflnLgTd6EOK0853j4= -github.com/temporalio/sqlparser v0.1.0/go.mod h1:143qKdh3G45IgV9p+gbAwp3ikRDI8mxsijFiXDfuxsw= -github.com/temporalio/tchannel-go v1.22.1/go.mod h1:ezRQRwu9KQXy8Wuuv1aaFFxoCNz5CeNbVOOkh3xctbY= -github.com/tidwall/btree v1.8.1/go.mod h1:jBbTdUWhSZClZWoDg54VnvV7/54modSOzDN7VXftj1A= -github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nEnL/aZY+0IuI4= -github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ= -github.com/twmb/murmur3 v1.1.8/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= -github.com/uber-common/bark v1.3.0/go.mod h1:5fDe/YcIVP55XhFF9hUihX2lDsDcpFrTZEAwAVwtPDw= -github.com/uber-go/tally/v4 v4.1.17/go.mod h1:ZdpiHRGSa3z4NIAc1VlEH4SiknR885fOIF08xmS0gaU= -github.com/urfave/cli v1.22.17/go.mod h1:b0ht0aqgH/6pBYzzxURyrM4xXNgsoT/n2ZzwQiEhNVo= -github.com/urfave/cli/v2 v2.27.7/go.mod h1:CyNAG/xg+iAOg0N4MPGZqVmv2rCoP267496AOXUZjA4= -github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= -github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= -github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +github.com/wenlong-gu/api-go v1.63.7-0.20261002150604-6b55806007ed h1:Jr0wN0pOdf8USIjmOL/v7t8P1dH1oBnnrMsZhaM3klg= +github.com/wenlong-gu/api-go v1.63.7-0.20261002150604-6b55806007ed/go.mod h1:acM0I9WPuYg8W3Pd9jOZvEgi7mRUttUQ4+e7fowKVnM= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= -go.opentelemetry.io/collector/featuregate v1.56.0/go.mod h1:4ga1QBMPEejXXmpyJS8lmaRpknJ3Lb9Bvk6e420bUFU= -go.opentelemetry.io/collector/pdata v1.56.0/go.mod h1:usR9utboXufbD1rp1oJy+3smQXXpZ+CsI3WN7QsiOs0= -go.opentelemetry.io/contrib/detectors/gcp v1.44.0/go.mod h1:tNAsgd8avTGke1+MndXlU5Cru4PQ9Ai/cCNWQv/ZJ/s= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.68.0/go.mod h1:Sje3i3MjSPKTSPvVWCaL8ugBzJwik3u4smCjUeuupqg= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0/go.mod h1:BuhAPThV8PBHBvg8ZzZ/Ok3idOdhWIodywz2xEcRbJo= go.opentelemetry.io/otel v1.44.0 h1:JjwHmHpA4iZ3wBxluu2fbbE7j4kqlE8jXyAyPXH7HqU= go.opentelemetry.io/otel v1.44.0/go.mod h1:BMgjTHL9WPRlRjL2oZCBTL4whCGtXch2H4BhOPIAyYc= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.43.0/go.mod h1:2lmweYCiHYpEjQ/lSJBYhj9jP1zvCvQW4BqL9dnT7FQ= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0/go.mod h1:Vl1/iaggsuRlrHf/hfPJPvVag77kKyvrLeD10kpMl+A= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0/go.mod h1:AGmbycVGEsRx9mXMZ75CsOyhSP6MFIcj/6dnG+vhVjk= -go.opentelemetry.io/otel/exporters/prometheus v0.56.0/go.mod h1:JQcVZtbIIPM+7SWBB+T6FK+xunlyidwLp++fN0sUaOk= -go.opentelemetry.io/otel/log v0.20.0/go.mod h1:wOcMcjsZpG8x7Bak7IhSi/lg8wscV2C1VdrKCLPlt0E= go.opentelemetry.io/otel/metric v1.44.0 h1:1w0gILTcHdr3YI+ixLyjemwrVnsMURbTZFrSYCdDdmc= go.opentelemetry.io/otel/metric v1.44.0/go.mod h1:8O7hanEPBNgEMmybD3s2VBKcgWOCsA6tzHBPODAiquo= go.opentelemetry.io/otel/sdk v1.44.0 h1:nHYwb9lK+fJPU/dnT6s7W7Z8itMWyqrnVfbheVYrZ58= @@ -233,42 +57,20 @@ go.opentelemetry.io/otel/sdk/metric v1.44.0 h1:3LlKgI+VjbVsjNRFZJZAJ30WjXC5VkNRk go.opentelemetry.io/otel/sdk/metric v1.44.0/go.mod h1:5B5pMARnXxKhltooO4xUuCBorl65a4EpnTalObqOigA= go.opentelemetry.io/otel/trace v1.44.0 h1:jxF5CsGYCe74MCRx2X4g7WsY/VBKRqqpNvXlX/6gtIk= go.opentelemetry.io/otel/trace v1.44.0/go.mod h1:oLl1jrMQAVo6v3GAggN+1VH9VIz9iUSvW53sW1Q8PIE= -go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk= -go.temporal.io/api v1.63.6-0.20260909222256-20151aa90480 h1:1w2xEZFpL7R9NagEQeWTh7oZmZU3YUJGogusn9/LfCg= -go.temporal.io/api v1.63.6-0.20260909222256-20151aa90480/go.mod h1:acM0I9WPuYg8W3Pd9jOZvEgi7mRUttUQ4+e7fowKVnM= -go.temporal.io/auto-scaled-workers v0.0.0-20260811170210-91f6fe1d10ab/go.mod h1:hhHijO9XRPIkAflLJJHix61M9FzbRPqk8fSydkcLkqw= -go.temporal.io/sdk v1.42.0/go.mod h1:Xp4TMHsie6kdw0lc0Ae4o8vktze5HZXBynF2DkiXcrQ= -go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.uber.org/dig v1.19.0/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE= -go.uber.org/fx v1.24.0/go.mod h1:AmDeGyS+ZARGKM4tlH4FY2Jr63VjbEDJHtqXTGP5hbo= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= -go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= -go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= -golang.org/x/crypto v0.55.0/go.mod h1:uq0V9dE/fzQuJtbnL+2EhWOE63vo164FY8xqEnV9xis= -golang.org/x/exp v0.0.0-20260611194520-c48552f49976/go.mod h1:vnf4pv9iKZXY58sQE1L86zmNWJ4159e1RkcWiLCkeEY= -golang.org/x/mod v0.38.0/go.mod h1:V6Xz0pq8TQ3dGqVQ1FVHuelZpAL0uNhSkk9ogYP3c40= golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= -golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= -golang.org/x/perf v0.0.0-20260709024250-82a0b07e230d/go.mod h1:N6rE9oqNql+XyUUA7hIpm+CorIvF5RWMdE7+p0BiB5s= -golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= -golang.org/x/term v0.45.0/go.mod h1:9aqxs0blBcrm/n0L9QW0aRVD+ktan8ssZromtqJC43w= golang.org/x/text v0.41.0 h1:vz/seA0lnX87Othu2f/0L24RcgrXD9/YFTSuGjj3rH8= golang.org/x/text v0.41.0/go.mod h1:jvf1O8ajNzZqhSrQBPbutR/EB83Cc0CFrezNQIwbb5M= -golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= -golang.org/x/tools v0.48.0/go.mod h1:08xX0orndb/F7jJxGDicx061tyd5pcMto75YMAXr6lk= gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= -google.golang.org/api v0.276.0/go.mod h1:Fnag/EWUPIcJXuIkP1pjoTgS5vdxlk3eeemL7Do6bvw= -google.golang.org/genproto v0.0.0-20260420184626-e10c466a9529/go.mod h1:EjLmDZ8liSLBrCTK5vP+bGIxRQHE3ovGvOI0CzGk1PI= google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa h1:Kjn0N0tCrDgiAFW+lGO4JZ3ck44CehvJQMAwj9QF0G8= google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:q4lMZS6kskjT5HvCPrnnypcDPVJqT/f4nfxmkE7gryY= google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa h1:mZHHdPZl0dbGHCflZgAq/Q468DWVFcU2whhB2KAo8fk= @@ -280,22 +82,5 @@ google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/evanphx/json-patch.v4 v4.13.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= -gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/validator.v2 v2.0.1/go.mod h1:lIUZBlB3Im4s/eYp39Ry/wkR02yOPhZ9IwIRBjuPuG8= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/api v0.35.4/go.mod h1:yl4lqySWOgYJJf9RERXKUwE9g2y+CkuwG+xmcOK8wXU= -k8s.io/apimachinery v0.35.4/go.mod h1:NNi1taPOpep0jOj+oRha3mBJPqvi0hGdaV8TCqGQ+cc= -k8s.io/client-go v0.35.4/go.mod h1:2Pg9WpsS4NeOpoYTfHHfMxBG8zFMSAUi4O/qoiJC3nY= -k8s.io/klog/v2 v2.140.0/go.mod h1:o+/RWfJ6PwpnFn7OyAG3QnO47BFsymfEfrz6XyYSSp0= -k8s.io/kube-openapi v0.0.0-20260414162039-ec9c827d403f/go.mod h1:uGBT7iTA6c6MvqUvSXIaYZo9ukscABYi2btjhvgKGZ0= -k8s.io/utils v0.0.0-20260319190234-28399d86e0b5/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk= -modernc.org/libc v1.72.3/go.mod h1:dn0dZNnnn1clLyvRxLxYExxiKRZIRENOfqQ8XEeg4Qs= -modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg= -modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw= -modernc.org/sqlite v1.51.0/go.mod h1:tcNzv5p84E0skkmJn038y+hWJbLQXQqEnQfeh5r2JLM= -sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= -sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= -sigs.k8s.io/structured-merge-diff/v6 v6.4.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= -sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=