Skip to content

Commit 8def786

Browse files
[Scheduled Actions] Limit the length of the ScheduleInfo Spec fields in the memo block (ListSchedules) (temporalio#7356)
## What changed? - The Spec fields on `ScheduleInfo` have an upper bound applied to their length with the LimitMemoSpecSize version applied. ## Why? - Large ScheduleInfo blocks in the memo lead to unnecessarily large database transactions. Customers can still use `DescribeSchedule` to get the full spec. ## How did you test it? - New functional test. - `go test -tags test_dep -v ./tests/ -run ScheduleFunctionalSuite/TestLimitMemoSpecSize && make lint` ## Potential risks - We break a customer who'd been relying on an exhaustive ScheduleInfo block in ListSchedules responses upon rollout.
1 parent 274a064 commit 8def786

File tree

2 files changed

+87
-0
lines changed

2 files changed

+87
-0
lines changed

service/worker/scheduler/workflow.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ const (
8484
AccurateFutureActionTimes = 9
8585
// include WorkflowExecutionStatus in ScheduleActionResult
8686
ActionResultIncludesStatus = 10
87+
// limit the ScheduleSpec specs and exclusions to only 10 entries
88+
LimitMemoSpecSize = 11
8789
)
8890

8991
const (
@@ -172,6 +174,7 @@ type (
172174
AllowZeroSleep bool // Whether to allow a zero-length timer. Used for workflow compatibility.
173175
ReuseTimer bool // Whether to reuse timer. Used for workflow compatibility.
174176
NextTimeCacheV2Size int // Size of next time cache (v2)
177+
SpecFieldLengthLimit int // item limit per spec field on the ScheduleInfo memo
175178
Version SchedulerWorkflowVersion // Used to keep track of schedules version to release new features and for backward compatibility
176179
// version 0 corresponds to the schedule version that comes before introducing the Version parameter
177180

@@ -221,6 +224,7 @@ var (
221224
AllowZeroSleep: true,
222225
ReuseTimer: true,
223226
NextTimeCacheV2Size: 14, // see note below
227+
SpecFieldLengthLimit: 10,
224228
Version: ActionResultIncludesStatus,
225229
}
226230

@@ -1038,6 +1042,14 @@ func (s *scheduler) getListInfo(inWorkflowContext bool) *schedulepb.ScheduleList
10381042
// clear fields that are too large/not useful for the list view
10391043
spec.TimezoneData = nil
10401044

1045+
if s.hasMinVersion(LimitMemoSpecSize) {
1046+
// Limit the number of specs and exclusions stored on the memo.
1047+
limit := s.tweakables.SpecFieldLengthLimit
1048+
spec.ExcludeStructuredCalendar = util.SliceHead(spec.ExcludeStructuredCalendar, limit)
1049+
spec.Interval = util.SliceHead(spec.Interval, limit)
1050+
spec.StructuredCalendar = util.SliceHead(spec.StructuredCalendar, limit)
1051+
}
1052+
10411053
return &schedulepb.ScheduleListInfo{
10421054
Spec: spec,
10431055
WorkflowType: s.Schedule.Action.GetStartWorkflow().GetWorkflowType(),

tests/schedule_test.go

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -981,6 +981,81 @@ func (s *ScheduleFunctionalSuite) TestListSchedulesReturnsWorkflowStatus() {
981981
s.assertSameRecentActions(descResp, listResp)
982982
}
983983

984+
// A schedule's memo should have an upper bound on the number of spec items stored.
985+
func (s *ScheduleFunctionalSuite) TestLimitMemoSpecSize() {
986+
// TODO - remove when MemoSpecFieldLimit becomes the default.
987+
prevTweakables := scheduler.CurrentTweakablePolicies
988+
scheduler.CurrentTweakablePolicies.Version = scheduler.LimitMemoSpecSize
989+
defer func() { scheduler.CurrentTweakablePolicies = prevTweakables }()
990+
991+
expectedLimit := scheduler.CurrentTweakablePolicies.SpecFieldLengthLimit
992+
993+
sid := "sched-test-limit-memo-size"
994+
wid := "sched-test-limit-memo-size-wf"
995+
wt := "sched-test-limit-memo-size-wt"
996+
997+
schedule := &schedulepb.Schedule{
998+
Spec: &schedulepb.ScheduleSpec{},
999+
Action: &schedulepb.ScheduleAction{
1000+
Action: &schedulepb.ScheduleAction_StartWorkflow{
1001+
StartWorkflow: &workflowpb.NewWorkflowExecutionInfo{
1002+
WorkflowId: wid,
1003+
WorkflowType: &commonpb.WorkflowType{Name: wt},
1004+
TaskQueue: &taskqueuepb.TaskQueue{Name: s.taskQueue, Kind: enumspb.TASK_QUEUE_KIND_NORMAL},
1005+
},
1006+
},
1007+
},
1008+
}
1009+
1010+
// Set up a schedule with a large number of spec items that should be trimmed in
1011+
// the memo block.
1012+
for i := 0; i < expectedLimit*2; i++ {
1013+
schedule.Spec.Interval = append(schedule.Spec.Interval, &schedulepb.IntervalSpec{
1014+
Interval: durationpb.New(time.Duration(i+1) * time.Second),
1015+
})
1016+
schedule.Spec.StructuredCalendar = append(schedule.Spec.StructuredCalendar, &schedulepb.StructuredCalendarSpec{
1017+
Minute: []*schedulepb.Range{
1018+
{
1019+
Start: int32(i + 1),
1020+
End: int32(i + 1),
1021+
},
1022+
},
1023+
})
1024+
schedule.Spec.ExcludeStructuredCalendar = append(schedule.Spec.ExcludeStructuredCalendar, &schedulepb.StructuredCalendarSpec{
1025+
Second: []*schedulepb.Range{
1026+
{
1027+
Start: int32(i + 1),
1028+
End: int32(i + 1),
1029+
},
1030+
},
1031+
})
1032+
}
1033+
1034+
// Create the schedule.
1035+
req := &workflowservice.CreateScheduleRequest{
1036+
Namespace: s.Namespace().String(),
1037+
ScheduleId: sid,
1038+
Schedule: schedule,
1039+
Identity: "test",
1040+
RequestId: uuid.New(),
1041+
}
1042+
s.worker.RegisterWorkflowWithOptions(
1043+
func(ctx workflow.Context) error { return nil },
1044+
workflow.RegisterOptions{Name: wt},
1045+
)
1046+
_, err := s.FrontendClient().CreateSchedule(testcore.NewContext(), req)
1047+
s.NoError(err)
1048+
s.cleanup(sid)
1049+
1050+
// Verify the memo field length limit was enforced.
1051+
entry := s.getScheduleEntryFomVisibility(sid, nil)
1052+
s.Require().NotNil(entry)
1053+
spec := entry.GetInfo().GetSpec()
1054+
s.Require().Equal(expectedLimit, len(spec.GetInterval()))
1055+
s.Require().Equal(expectedLimit, len(spec.GetStructuredCalendar()))
1056+
s.Require().Equal(expectedLimit, len(spec.GetExcludeStructuredCalendar()))
1057+
}
1058+
9841059
func (s *ScheduleFunctionalSuite) TestNextTimeCache() {
9851060
sid := "sched-test-next-time-cache"
9861061
wid := "sched-test-next-time-cache-wf"

0 commit comments

Comments
 (0)