retry.go ×15

Frontier kind: Joint frontier

unlabeled · c_9b4b5a27c06b

1 test · 10525 LOC · 274 files · introduces 1 test · 261 LOC · 9 files

Introduces — evidence that enters the hierarchy at this concept

Code
45 ranges261 lines · 9 files
Tests
1 test

Contains — complete concept membership

All code (extent)
2301 ranges10525 lines · 274 files · Browse complete extent
All tests (intent)
1 testBrowse complete intent

Neighbourhood graph

The orange circle is the focus. Violet and green circles are every ancestor and descendant, broader and narrower, at any distance; blue squares and pink diamonds are the introduced files and exact introduced tests of every visible concept, not only the focus's. Arrows point from broader to narrower concepts and bridge only concepts omitted from this view. Undirected links show source or test introduction. Concept and file size follows LOC; exact test nodes use test-count units.

Introduced files, introduced tests, and structurally relevant concept specialization

In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the native relationship evidence on this page.

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the native relationship evidence on this page while the interactive map is unavailable.

Native relationship evidence

Every exact file and test below is linked only from the concept that introduces it.

Introduced tests

Every collected test enters the hierarchy at exactly one concept.

1 test introduced at this concept.

Introduced code

Every collected source range enters the hierarchy at exactly one concept.

9 files ranked by introduced lines: 261 introduced LOC across 45 ranges. Expand a file to inspect source; the > gutter marks introduced lines.

go.temporal.io/server/service/history/workflow/retry.go 129 introduced LOC · 15 ranges

Open complete file

165 backoffInterval time.Duration,
166 initiator enumspb.ContinueAsNewInitiator,
167 > ) error { retry.go
168 >
169 > // Extract ParentExecutionInfo and RootExecutionInfo from current run so it can be passed down to the next
170 > var parentInfo *workflowspb.ParentExecutionInfo
171 > var rootInfo *workflowspb.RootExecutionInfo
172 > previousExecutionInfo := previousMutableState.GetExecutionInfo()
173 > if previousMutableState.HasParentExecution() {
174 parentInfo = &workflowspb.ParentExecutionInfo{
175 NamespaceId: previousExecutionInfo.ParentNamespaceId,
191 }
192
193 > newExecution := commonpb.WorkflowExecution{ retry.go
194 > WorkflowId: previousExecutionInfo.WorkflowId,
195 > RunId: newRunID,
196 > }
197 >
198 > firstRunID, err := previousMutableState.GetFirstRunID(ctx)
199 > if err != nil {
200 return err
201 }
202
203 > taskQueue := previousExecutionInfo.TaskQueue retry.go
204 > if startAttr.TaskQueue != nil {
205 > taskQueue = startAttr.TaskQueue.GetName()
206 > }
207 > tq := &taskqueuepb.TaskQueue{
208 > Name: taskQueue,
209 > Kind: enumspb.TASK_QUEUE_KIND_NORMAL,
210 > }
211 >
212 > workflowType := previousExecutionInfo.WorkflowTypeName
213 > if startAttr.WorkflowType != nil {
214 > workflowType = startAttr.WorkflowType.GetName()
215 > }
216 > wType := &commonpb.WorkflowType{
217 > Name: workflowType,
218 > }
219 >
220 > var taskTimeout *durationpb.Duration
221 > if timestamp.DurationValue(startAttr.GetWorkflowTaskTimeout()) == 0 {
222 taskTimeout = previousExecutionInfo.DefaultWorkflowTaskTimeout
223 > } else { retry.go
224 > taskTimeout = startAttr.GetWorkflowTaskTimeout()
225 > }
226
227 // Workflow runTimeout is already set to the correct value in
228 // validateContinueAsNewWorkflowExecutionAttributes
229 > runTimeout := startAttr.GetWorkflowRunTimeout() retry.go
230 >
231 > var completionCallbacks []*commonpb.Callback
232 > if initiator == enumspb.CONTINUE_AS_NEW_INITIATOR_RETRY {
233 > completionCallbacks, err = getCompletionCallbacksAsProtoSlice(ctx, previousMutableState)
234 > if err != nil {
235 return err
236 }
237 }
238
239 > var pinnedOverride *workflowpb.VersioningOverride retry.go
240 > if o := previousExecutionInfo.GetVersioningInfo().GetVersioningOverride(); worker_versioning.OverrideIsPinned(o) {
241 pinnedOverride = o
242 // retries and crons always go to the same task queue, so no need to check if override version is in new task queue
248 // of retry, and the retried run inherited a pinned version when it started (ie. it is a child of a pinned
249 // parent, or a CaN of a pinned run, and is running on a Task Queue in the inherited version).
250 > var inheritedPinnedVersion *deploymentpb.WorkerDeploymentVersion retry.go
251 > // If the previous run had an AutoUpgrade behavior, we pass down the source deployment version and revision number to the new run.
252 > // Note: We only pass down one of inheritedPinnedVersion or inheritedAutoUpgradeInfo, but not both!
253 > var inheritedAutoUpgradeInfo *deploymentpb.InheritedAutoUpgradeInfo
254 >
255 > if initiator == enumspb.CONTINUE_AS_NEW_INITIATOR_RETRY {
256 > // retries and crons always go to the same task queue, so no need to check if override version is in new task queue
257 >
258 > prevEffectiveVersioningBehavior := GetEffectiveVersioningBehavior(previousExecutionInfo.GetVersioningInfo())
259 > if prevEffectiveVersioningBehavior == enumspb.VERSIONING_BEHAVIOR_PINNED &&
260 > startAttr.GetInheritedPinnedVersion() != nil {
261 inheritedPinnedVersion = worker_versioning.ExternalWorkerDeploymentVersionFromDeployment(GetEffectiveDeployment(previousExecutionInfo.GetVersioningInfo()))
262 > } else if prevEffectiveVersioningBehavior == enumspb.VERSIONING_BEHAVIOR_AUTO_UPGRADE { retry.go
263 sourceDeploymentVersion := worker_versioning.ExternalWorkerDeploymentVersionFromDeployment(previousMutableState.GetEffectiveDeployment())
264 sourceDeploymentRevisionNumber := previousMutableState.GetVersioningRevisionNumber()
283 }
284
285 > createRequest := &workflowservice.StartWorkflowExecutionRequest{ retry.go
286 > RequestId: uuid.NewString(),
287 > Namespace: newMutableState.GetNamespaceEntry().Name().String(),
288 > WorkflowId: newExecution.WorkflowId,
289 > TaskQueue: tq,
290 > WorkflowType: wType,
291 > WorkflowExecutionTimeout: previousExecutionInfo.WorkflowExecutionTimeout,
292 > WorkflowRunTimeout: runTimeout,
293 > WorkflowTaskTimeout: taskTimeout,
294 > Input: startAttr.Input,
295 > Header: startAttr.Header,
296 > RetryPolicy: startAttr.RetryPolicy,
297 > CronSchedule: startAttr.CronSchedule,
298 > Memo: startAttr.Memo,
299 > SearchAttributes: startAttr.SearchAttributes,
300 > CompletionCallbacks: completionCallbacks,
301 > Links: startLinks,
302 > Priority: startAttr.Priority,
303 > VersioningOverride: pinnedOverride,
304 > }
305 >
306 > tsc, stateProp := propagateTimeSkippingToNextRun(previousExecutionInfo)
307 > createRequest.TimeSkippingConfig = tsc
308 >
309 > attempt := int32(1)
310 > if initiator == enumspb.CONTINUE_AS_NEW_INITIATOR_RETRY {
311 > attempt = previousExecutionInfo.Attempt + 1
312 > }
313
314 > var sourceVersionStamp *commonpb.WorkerVersionStamp retry.go
315 > if previousExecutionInfo.AssignedBuildId == "" && GetEffectiveVersioningBehavior(previousExecutionInfo.GetVersioningInfo()) == enumspb.VERSIONING_BEHAVIOR_UNSPECIFIED {
316 > // TODO: only keeping this part for old versioning. The desired logic seem to be the same for both cron and
317 > // retry: keep originally-inherited build ID. [cleanup-old-wv]
318 > // For retry: propagate build-id version info to new workflow.
319 > // For cron: do not propagate (always start on latest version).
320 > if initiator == enumspb.CONTINUE_AS_NEW_INITIATOR_RETRY {
321 > sourceVersionStamp = worker_versioning.StampIfUsingVersioning(previousMutableState.GetMostRecentWorkerVersionStamp())
322 > }
323 }
324
325 > req := &historyservice.StartWorkflowExecutionRequest{ retry.go
326 > NamespaceId: newMutableState.GetNamespaceEntry().ID().String(),
327 > StartRequest: createRequest,
328 > ParentExecutionInfo: parentInfo,
329 > LastCompletionResult: lastCompletionResult,
330 > ContinuedFailure: failure,
331 > ContinueAsNewInitiator: initiator,
332 > // enforce minimal interval between runs to prevent tight loop continue as new spin.
333 > FirstWorkflowTaskBackoff: previousMutableState.ContinueAsNewMinBackoff(durationpb.New(backoffInterval)),
334 > Attempt: attempt,
335 > SourceVersionStamp: sourceVersionStamp,
336 > RootExecutionInfo: rootInfo,
337 > InheritedBuildId: startAttr.InheritedBuildId, //nolint:staticcheck
338 > InheritedPinnedVersion: inheritedPinnedVersion,
339 > InheritedAutoUpgradeInfo: inheritedAutoUpgradeInfo,
340 > // For retries, pass through the declined value from the started event directly.
341 > DeclinedTargetVersionUpgrade: startAttr.GetDeclinedTargetVersionUpgrade(),
342 > // Carry the previous run's accumulated skip and fast-forward target forward.
343 > TimeSkippingStatePropagation: stateProp,
344 > }
345 > workflowTimeoutTime := timestamp.TimeValue(previousExecutionInfo.WorkflowExecutionExpirationTime)
346 > if !workflowTimeoutTime.IsZero() {
347 > req.WorkflowExecutionExpirationTime = timestamppb.New(workflowTimeoutTime)
348 > }
349
350 > event, err := newMutableState.AddWorkflowExecutionStartedEventWithOptions( retry.go
351 > &newExecution,
352 > req,
353 > previousExecutionInfo.AutoResetPoints,
354 > previousMutableState.GetExecutionState().GetRunId(),
355 > firstRunID,
356 > )
357 > if err != nil {
358 return serviceerror.NewInternal("Failed to add workflow execution started event.")
359 }
360 > var parentClock *clockspb.VectorClock retry.go
361 > if parentInfo != nil {
362 parentClock = parentInfo.Clock
363 }
364 > if _, err = newMutableState.AddFirstWorkflowTaskScheduled(parentClock, event, false); err != nil { retry.go
365 return err
366 }
367
368 > return nil retry.go
369 }
go.temporal.io/server/service/history/timer_queue_active_task_executor.go 63 introduced LOC · 8 ranges

Open complete file

692 backoffInterval, retryState = mutableState.GetRetryBackoffDuration(timeoutFailure)
693 if backoffInterval != backoff.NoBackoff {
694 > // We have a retry policy and we should retry. timer_queue_active_task_executor.go
695 > initiator = enumspb.CONTINUE_AS_NEW_INITIATOR_RETRY
696 } else if backoffInterval = mutableState.GetCronBackoffDuration(); backoffInterval != backoff.NoBackoff {
697 // We have a cron schedule.
702 var newRunID string
703 if initiator != enumspb.CONTINUE_AS_NEW_INITIATOR_UNSPECIFIED {
704 > newRunID = uuid.NewString() timer_queue_active_task_executor.go
705 > }
706
707 // First add timeout workflow event, no matter what we're doing next.
726 }
727
728 > startEvent, err := mutableState.GetStartEvent(ctx) timer_queue_active_task_executor.go
729 > if err != nil {
730 return err
731 }
732 > startAttr := startEvent.GetWorkflowExecutionStartedEventAttributes() timer_queue_active_task_executor.go
733 >
734 > // TODO@time-skipping: if time skipping happened, the virtual time is
735 > // propagated to the new mutable state, need to check the fast-forward works correctly in the retry.
736 > newMutableState, err := workflow.NewMutableStateInChain(
737 > t.shardContext,
738 > t.shardContext.GetEventsCache(),
739 > t.shardContext.GetLogger(),
740 > mutableState.GetNamespaceEntry(),
741 > mutableState.GetWorkflowKey().WorkflowID,
742 > newRunID,
743 > mutableState.Now(),
744 > mutableState,
745 > )
746 > if err != nil {
747 return err
748 }
749
750 > err = workflow.SetupNewWorkflowForRetryOrCron( timer_queue_active_task_executor.go
751 > ctx,
752 > mutableState,
753 > newMutableState,
754 > newRunID,
755 > startAttr,
756 > startEvent.Links,
757 > startAttr.LastCompletionResult,
758 > timeoutFailure,
759 > backoffInterval,
760 > initiator,
761 > )
762 > if err != nil {
763 return err
764 }
765
766 > err = newMutableState.SetHistoryTree( timer_queue_active_task_executor.go
767 > newMutableState.GetExecutionInfo().WorkflowExecutionTimeout,
768 > newMutableState.GetExecutionInfo().WorkflowRunTimeout,
769 > newRunID,
770 > )
771 > if err != nil {
772 return err
773 }
774
775 > newExecutionInfo := newMutableState.GetExecutionInfo() timer_queue_active_task_executor.go
776 > newExecutionState := newMutableState.GetExecutionState()
777 > updateErr := weContext.UpdateWorkflowExecutionWithNewAsActive(
778 > ctx,
779 > t.shardContext,
780 > workflow.NewContext(
781 > t.shardContext.GetConfig(),
782 > definition.NewWorkflowKey(
783 > newExecutionInfo.NamespaceId,
784 > newExecutionInfo.WorkflowId,
785 > newExecutionState.RunId,
786 > ),
787 > chasm.WorkflowArchetypeID,
788 > t.logger,
789 > t.shardContext.GetThrottledLogger(),
790 > t.shardContext.GetMetricsHandler(),
791 > ),
792 > newMutableState,
793 > )
794 >
795 > if updateErr != nil {
796 return updateErr
797 }
800 // for this WF are aborted with a retryable error.
801 // Internal server retries will retry the API call, and the Update will be sent to the new run.
802 > weContext.UpdateRegistry(ctx).Abort(update.AbortReasonWorkflowContinuing) timer_queue_active_task_executor.go
803 > return nil
804 }
805
go.temporal.io/server/service/history/workflow/mutable_state_impl.go 44 introduced LOC · 10 ranges

Open complete file

1765 // todo@time-skipping: time skipping is naturally supported for cron backoff, and need to
1766 // confirm it is the best default policy for cron
1767 > return getBackoffInterval( mutable_state_impl.go
1768 > ms.timeSource.Now(),
1769 > info.Attempt,
1770 > info.RetryMaximumAttempts,
1771 > info.RetryInitialInterval,
1772 > info.RetryMaximumInterval,
1773 > info.WorkflowExecutionExpirationTime,
1774 > info.RetryBackoffCoefficient,
1775 > failure,
1776 > info.RetryNonRetryableErrorTypes,
1777 > )
1778 }
1779
2860 }
2861
2862 > func (ms *MutableStateImpl) ContinueAsNewMinBackoff(backoffDuration *durationpb.Duration) *durationpb.Duration { mutable_state_impl.go
2863 > // lifetime of previous execution
2864 > // todo@time-skipping: time skipping is naturally supported for continue as new backoff, and need to
2865 > // make sure the backoff is correctly applied in the time skipping case
2866 > lifetime := ms.timeSource.Now().Sub(ms.executionState.StartTime.AsTime().UTC())
2867 > if ms.executionInfo.ExecutionTime != nil {
2868 > lifetime = ms.timeSource.Now().Sub(ms.executionInfo.ExecutionTime.AsTime().UTC())
2869 > }
2870
2871 > interval := lifetime mutable_state_impl.go
2872 > if backoffDuration != nil {
2873 > // already has a backoff, add it to interval
2874 > interval += backoffDuration.AsDuration()
2875 > }
2876 // minimal interval for continue as new to prevent tight continue as new loop
2877 > minInterval := ms.config.WorkflowIdReuseMinimalInterval(ms.namespaceEntry.Name().String()) mutable_state_impl.go
2878 > if interval < minInterval {
2879 // enforce a minimal backoff
2880 return durationpb.New(minInterval - lifetime)
2881 }
2882
2883 > return backoffDuration mutable_state_impl.go
2884 }
2885
4965 // Workflow-level callbacks are inherited by the retry run, but update callbacks
4966 // must fire now because the update was aborted on the old run.
4967 > return ms.processUpdateCloseCallbacks() mutable_state_impl.go
4968 }
4969
6309 return resetPoints
6310 }
6311 > newPoints := make([]*workflowpb.ResetPointInfo, 0, len(resetPoints.Points)) mutable_state_impl.go
6312 > // For continue-as-new, new execution start time is the same as previous execution close time,
6313 > // so reset points from the previous run will expire at new execution start time plus retention.
6314 > expireTime := newExecutionStartTime.Add(namespaceRetention)
6315 > for _, rp := range resetPoints.Points {
6316 > if rp.ExpireTime != nil && rp.ExpireTime.AsTime().Before(newExecutionStartTime) {
6317 continue // run is expired, don't preserve it
6318 }
6319 > if rp.GetRunId() == prevRunID { mutable_state_impl.go
6320 > rp.ExpireTime = timestamppb.New(expireTime)
6321 > }
6322 > newPoints = append(newPoints, rp)
6323 }
6324 > return &workflowpb.ResetPoints{Points: newPoints} mutable_state_impl.go
6325 }
6326
7166 // Reset always terminates the old run (via terminateWorkflow), which goes through
7167 // processCloseCallbacks — not through the retry/CAN paths that call this method.
7168 > func (ms *MutableStateImpl) processUpdateCloseCallbacks() error { mutable_state_impl.go
7169 > if !ms.ChasmEnabled() {
7170 > // Update callbacks are only supported in CHASM mode.
7171 > return nil
7172 > }
7173
7174 wf, _, err := ms.ChasmWorkflowComponentReadOnly(context.Background())
go.temporal.io/server/service/history/workflow/timeskipping.go 10 introduced LOC · 4 ranges

Open complete file

131 func propagateTimeSkippingToNextRun(
132 source *persistencespb.WorkflowExecutionInfo,
133 > ) (*commonpb.TimeSkippingConfig, *commonpb.TimeSkippingStatePropagation) { timeskipping.go
134 > previousTSC := source.GetTimeSkippingInfo().GetConfig()
135 >
136 > // if disabled, we just return nil for the new TSC
137 > var newTSC *commonpb.TimeSkippingConfig
138 > if previousTSC.GetEnabled() {
139 newTSC = common.CloneProto(previousTSC)
140 }
141
142 > var stateProp *commonpb.TimeSkippingStatePropagation timeskipping.go
143 > if accum := accumulatedSkippedDuration(source); accum > 0 {
144 stateProp = &commonpb.TimeSkippingStatePropagation{
145 InitialSkippedDuration: durationpb.New(accum),
147 }
148
149 > if ff := source.GetTimeSkippingInfo().GetFastForwardInfo(); ff != nil && !ff.GetHasReached() { timeskipping.go
150 if stateProp == nil {
151 stateProp = &commonpb.TimeSkippingStatePropagation{}
153 stateProp.FastForwardTargetTime = ff.GetTargetTime()
154 }
155 > return newTSC, stateProp timeskipping.go
156 }
157
go.temporal.io/server/service/history/workflow/util.go 6 introduced LOC · 3 ranges

Open complete file

245 }
246
247 > func getCompletionCallbacksAsProtoSlice(ctx context.Context, ms historyi.MutableState) ([]*commonpb.Callback, error) { util.go
248 > coll := callbacks.MachineCollection(ms.HSM())
249 > result := make([]*commonpb.Callback, 0, coll.Size())
250 > for _, node := range coll.List() {
251 cb, err := coll.Data(node.Key.ID)
252 if err != nil {
264
265 // Collect CHASM callbacks
266 > if ms.ChasmEnabled() { util.go
267 wf, ctx, err := ms.ChasmWorkflowComponentReadOnly(ctx)
268 if err != nil {
286 // }
287
288 > return result, nil util.go
289 }
290
go.temporal.io/server/common/worker_versioning/worker_versioning.go 3 introduced LOC · 2 ranges

Open complete file

172 // StampIfUsingVersioning returns the given WorkerVersionStamp if it is using versioning,
173 // otherwise returns nil.
174 > func StampIfUsingVersioning(stamp *commonpb.WorkerVersionStamp) *commonpb.WorkerVersionStamp { worker_versioning.go
175 > if stamp.GetUseVersioning() {
176 return stamp
177 }
178 > return nil worker_versioning.go
179 }
180
go.temporal.io/server/service/history/tasks/workflow_execution_timer.go 3 introduced LOC · 1 range

Open complete file

50 }
51
52 > func (t *WorkflowExecutionTimeoutTask) SetTaskID(id int64) { workflow_execution_timer.go
53 > t.TaskID = id
54 > }
55
56 func (t *WorkflowExecutionTimeoutTask) GetVisibilityTime() time.Time {
go.temporal.io/server/service/history/workflow/task_generator.go 2 introduced LOC · 1 range

Open complete file

387 var workflowBackoffType enumsspb.WorkflowBackoffType
388 switch startAttr.GetInitiator() {
389 > case enumspb.CONTINUE_AS_NEW_INITIATOR_RETRY: task_generator.go
390 > workflowBackoffType = enumsspb.WORKFLOW_BACKOFF_TYPE_RETRY
391 case enumspb.CONTINUE_AS_NEW_INITIATOR_CRON_SCHEDULE, enumspb.CONTINUE_AS_NEW_INITIATOR_WORKFLOW:
392 workflowBackoffType = enumsspb.WORKFLOW_BACKOFF_TYPE_CRON
go.temporal.io/server/api/persistence/v1/executions.pb.go 1 introduced LOC · 1 range

Open complete file

1234 return x.FastForwardInfo
1235 }
1236 > return nil executions.pb.go
1237 }
1238