Skip to content

Commit 390149c

Browse files
committed
add activity ts feature flag
1 parent 317441c commit 390149c

5 files changed

Lines changed: 24 additions & 58 deletions

File tree

chasm/lib/activity/config.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,12 @@ var (
3939
false,
4040
`Allows attaching completion callbacks to standalone activity executions.`,
4141
)
42+
43+
TimeSkippingEnabled = dynamicconfig.NewNamespaceBoolSetting(
44+
"activity.timeSkippingEnabled",
45+
false,
46+
`Allows setting time_skipping_config on standalone activity executions. This is a standalone-activity-specific feature flag, separate from the workflow frontend.TimeSkippingEnabled flag.`,
47+
)
4248
)
4349

4450
type Config struct {
@@ -69,7 +75,7 @@ func ConfigProvider(dc *dynamicconfig.Collection) *Config {
6975
LongPollTimeout: LongPollTimeout.Get(dc),
7076
MaxIDLengthLimit: dynamicconfig.MaxIDLengthLimit.Get(dc),
7177
StartDelayEnabled: StartDelayEnabled.Get(dc),
72-
TimeSkippingEnabled: dynamicconfig.TimeSkippingEnabled.Get(dc),
78+
TimeSkippingEnabled: TimeSkippingEnabled.Get(dc),
7379
MaxCallbacksPerExecution: callback.MaxPerExecution.Get(dc),
7480
VisibilityMaxPageSize: dynamicconfig.FrontendVisibilityMaxPageSize.Get(dc),
7581
}

chasm/lib/activity/frontend.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,12 @@ func (h *frontendHandler) StartActivityExecution(ctx context.Context, req *workf
9292
if !h.config.Enabled(req.GetNamespace()) {
9393
return nil, ErrStandaloneActivityDisabled
9494
}
95+
if req.GetTimeSkippingConfig() != nil && !h.config.TimeSkippingEnabled(req.GetNamespace()) {
96+
return nil, serviceerror.NewUnimplementedf(
97+
"The Time-Skipping feature is not enabled for namespace %s",
98+
req.GetNamespace(),
99+
)
100+
}
95101

96102
namespaceID, err := h.namespaceRegistry.GetNamespaceID(namespace.Name(req.GetNamespace()))
97103
if err != nil {

chasm/tree.go

Lines changed: 2 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1730,6 +1730,8 @@ func (n *Node) CloseTransaction() (NodesMutation, error) {
17301730
return NodesMutation{}, err
17311731
}
17321732

1733+
// time skippign should be called at the end of the transaction,
1734+
// so that all invalid tasks are deleted.
17331735
if err := n.closeTransactionHandleTimeSkipping(); err != nil {
17341736
return NodesMutation{}, err
17351737
}
@@ -2197,29 +2199,6 @@ func (n *Node) closeTransactionHandleTimeSkipping() error {
21972199
return nil
21982200
}
21992201

2200-
// validateSerializedTask deserializes a serialized component task on this node and runs its validator
2201-
// against current component state, returning true only if the task is still valid. It mirrors the
2202-
// per-task validation in closeTransactionCleanupInvalidTasks and is used by the time-skipping paths
2203-
// (calculateTimeSkippingTransition, regenerateTasksForTimeSkipping) so that an invalidated task — e.g. a
2204-
// timeout whose activity already executed — is neither chosen as a skip target nor re-stamped. The component
2205-
// value is hydrated first (prepareComponentValue is idempotent).
2206-
func (n *Node) validateSerializedTask(
2207-
ctx Context,
2208-
task *persistencespb.ChasmComponentAttributes_Task,
2209-
) (bool, error) {
2210-
if err := n.prepareComponentValue(ctx); err != nil {
2211-
return false, err
2212-
}
2213-
taskInstance, err := n.deserializeComponentTask(task)
2214-
if err != nil {
2215-
return false, err
2216-
}
2217-
return n.validateTask(ctx, TaskAttributes{
2218-
ScheduledTime: task.GetScheduledTime().AsTime(),
2219-
Destination: task.GetDestination(),
2220-
}, taskInstance)
2221-
}
2222-
22232202
func (n *Node) defaultFindNextTargetTime(transition *TimeSkippingTransition) error {
22242203
now := transition.CurrentTime
22252204
for _, node := range n.andAllChildren() {
@@ -2246,16 +2225,6 @@ func (n *Node) defaultFindNextTargetTime(transition *TimeSkippingTransition) err
22462225
return nil
22472226
}
22482227

2249-
// regenerateChasmTasksForTimeSkipping re-emits physical timer tasks after a time-skipping transition
2250-
// so their wall-clock visibility reflects the new accumulated skip.
2251-
//
2252-
// "Regenerate all" by default: every CategoryTimer task (pure and side-effect) across the tree is
2253-
// reset to physicalTaskStatusNone and re-generated. BOTH pure and side-effect tasks can be
2254-
// future-scheduled (CategoryTimer) tasks that need re-stamping.Immediate Transfer/Outbound/Visibility
2255-
// tasks are left untouched.
2256-
//
2257-
// Invalidated tasks are excluded: each task is validated (validateSerializedTask) before being reset or
2258-
// re-stamped, so a task whose validator now rejects it is left alone rather than re-emitted.
22592228
func (n *Node) regenerateChasmTasksForTimeSkipping(ctx Context) error {
22602229
archetypeID := n.ArchetypeID()
22612230

@@ -2272,25 +2241,11 @@ func (n *Node) regenerateChasmTasksForTimeSkipping(ctx Context) error {
22722241
if taskCategory(sideEffectTask) != tasks.CategoryTimer {
22732242
continue
22742243
}
2275-
valid, err := node.validateSerializedTask(ctx, sideEffectTask)
2276-
if err != nil {
2277-
return err
2278-
}
2279-
if !valid {
2280-
continue
2281-
}
22822244
sideEffectTask.PhysicalTaskStatus = physicalTaskStatusNone
22832245
node.closeTransactionGeneratePhysicalSideEffectTask(sideEffectTask, nodePath, archetypeID)
22842246
}
22852247

22862248
for _, pureTask := range componentAttr.GetPureTasks() {
2287-
valid, err := node.validateSerializedTask(ctx, pureTask)
2288-
if err != nil {
2289-
return err
2290-
}
2291-
if !valid {
2292-
continue
2293-
}
22942249
pureTask.PhysicalTaskStatus = physicalTaskStatusNone
22952250
if firstPureTask == nil || comparePureTasks(pureTask, firstPureTask) < 0 {
22962251
firstPureTask = pureTask

service/history/workflow/timeskipping.go

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,7 @@ func (ms *MutableStateImpl) isWorkflowSkippable() bool {
302302
}
303303
// A pending activity blocks time skipping unless it has failed and is still
304304
// waiting out its retry backoff (next attempt strictly in the future) — that one
305-
// is a skip target, not in-flight work (see calculateTimeSkippingTransition). The
305+
// is a skip target, not in-flight work (see findNextSkipTarget). The
306306
// strict future check is what keeps a just-scheduled or already-due activity (next
307307
// attempt <= now) blocking.
308308
for _, ai := range ms.GetPendingActivityInfos() {
@@ -486,10 +486,9 @@ func (ms *MutableStateImpl) closeTransactionRegenTimerTasksForWorkflowTimeSkippi
486486

487487
// applyTimeSkippingTransition mutates the execution's TimeSkippingInfo for a single skip transition:
488488
// it advances AccumulatedSkippedDuration by (TargetTime - CurrentTime), toggles Config.Enabled, and
489-
// marks the fast-forward target reached. It is the archetype-agnostic core shared by the workflow
490-
// event path (ApplyWorkflowExecutionTimeSkippingTransitionedEvent) and the CHASM close-transaction
491-
// path (RecordTimeSkippingTransition); the workflow path records the transition as a history event while
492-
// CHASM (which has no history) applies it directly, but the TSI mutation is identical.
489+
// marks the fast-forward target reached. This is the CHASM (non-workflow) apply path, invoked by
490+
// RecordTimeSkippingTransition; the workflow path applies the equivalent mutation from its history
491+
// event in ApplyWorkflowExecutionTimeSkippingTransitionedEvent.
493492
//
494493
// A zero TargetTime means "no skip" (only valid together with DisabledAfterFastForward, e.g. a bound
495494
// hit exactly). transition.CurrentTime must be in the virtual frame (ms.Now() / the event's virtual
@@ -558,9 +557,9 @@ func (ms *MutableStateImpl) SetTimeSkippingConfig(config *commonpb.TimeSkippingC
558557
}
559558

560559
// RecordTimeSkippingTransition records a single time-skipping transition for the execution. It is the
561-
// archetype-agnostic apply sink shared by both time-skipping front-ends: the CHASM framework's
562-
// closeTransactionHandleTimeSkipping (which builds the transition for non-workflow CHASM executions)
563-
// and the workflow event path (closeTransactionHandleTimeSkipping -> shouldExecuteTimeSkipping). The
560+
// archetype-aware apply sink: the CHASM framework's closeTransactionHandleTimeSkipping calls it for
561+
// non-workflow executions after building the transition, and the fast-forward wake timer executor
562+
// calls it (for either archetype) to disable time skipping once the fast-forward target is hit. The
564563
// caller is responsible for computing the final transition — including the min of the component
565564
// candidate and the fast-forward target; this method only records it.
566565
//

tests/activity_standalone_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,8 +146,8 @@ func (s *standaloneActivityTestSuite) TestTimeSkipping_StartDelayAndRetryBackoff
146146
env := s.newTestEnv()
147147
t := s.T()
148148

149-
// Standalone activities only opt into time skipping when the namespace flag is on.
150-
env.GetTestCluster().OverrideDynamicConfig(t, dynamicconfig.TimeSkippingEnabled, []dynamicconfig.ConstrainedValue{
149+
// Standalone activities only opt into time skipping when the SAA-specific namespace flag is on.
150+
env.GetTestCluster().OverrideDynamicConfig(t, activity.TimeSkippingEnabled, []dynamicconfig.ConstrainedValue{
151151
{Constraints: dynamicconfig.Constraints{Namespace: env.Namespace().String()}, Value: true},
152152
})
153153

0 commit comments

Comments
 (0)