From 4381645247d5d28cd023b8c0deefcd345dcf7096 Mon Sep 17 00:00:00 2001 From: Jake Kaplan Date: Mon, 5 Dec 2022 10:26:48 -0500 Subject: [PATCH 1/6] add a max rrule length --- src/prefect/orion/schemas/schedules.py | 7 +++++++ tests/orion/schemas/test_schedules.py | 17 ++++++++++------- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/prefect/orion/schemas/schedules.py b/src/prefect/orion/schemas/schedules.py index 1a2df93c55b6..7437cf529b66 100644 --- a/src/prefect/orion/schemas/schedules.py +++ b/src/prefect/orion/schemas/schedules.py @@ -15,6 +15,8 @@ from prefect.orion.utilities.schemas import DateTimeTZ, PrefectBaseModel MAX_ITERATIONS = 1000 +# approx. a years worth of RDATEs + buffer +MAX_RRULE_LENGTH = 6500 def _prepare_scheduling_start_and_end( @@ -394,6 +396,11 @@ def validate_rrule_str(cls, v): # rrules errors are a mix of cryptic and informative # so reraise to be clear that the string was invalid raise ValueError(f'Invalid RRule string "{v}": {exc}') + if len(v) > MAX_RRULE_LENGTH: + raise ValueError( + f'Invalid RRule string "{v}"\n' + f"Max length is {MAX_RRULE_LENGTH}, got {len(v)}" + ) return v @classmethod diff --git a/tests/orion/schemas/test_schedules.py b/tests/orion/schemas/test_schedules.py index 39b7e50a17b9..c368bf4df7be 100644 --- a/tests/orion/schemas/test_schedules.py +++ b/tests/orion/schemas/test_schedules.py @@ -11,6 +11,7 @@ from prefect.orion.schemas.schedules import ( MAX_ITERATIONS, + MAX_RRULE_LENGTH, CronSchedule, IntervalSchedule, RRuleSchedule, @@ -634,13 +635,6 @@ async def test_rrule_returns_nothing_before_dtstart(self): dates = await s.get_dates(5, start=pendulum.now("UTC")) assert dates == [pendulum.datetime(2030, 1, 1).add(days=i) for i in range(5)] - async def test_rrule_returns_nothing_before_dtstart(self): - s = RRuleSchedule.from_rrule( - rrule.rrule(freq=rrule.DAILY, dtstart=pendulum.datetime(2030, 1, 1)) - ) - dates = await s.get_dates(5, start=pendulum.now("UTC")) - assert dates == [pendulum.datetime(2030, 1, 1).add(days=i) for i in range(5)] - async def test_rrule_validates_rrule_str(self): # generic validation error with pytest.raises(ValidationError, match="(Invalid RRule string)"): @@ -654,6 +648,15 @@ async def test_rrule_validates_rrule_str(self): with pytest.raises(ValidationError, match="(invalid 'FREQ': DAILYBAD)"): RRuleSchedule(rrule="FREQ=DAILYBAD") + async def test_rrule_max_rrule_len(self): + start = datetime(2000, 1, 1) + s = "RDATE:" + ",".join( + [start.add(days=i).format("YMMDD") + "T000000Z" for i in range(1000)] + ) + assert len(s) > MAX_RRULE_LENGTH + with pytest.raises(ValidationError, match="Max length"): + RRuleSchedule(rrule=s) + async def test_rrule_schedule_handles_complex_rrulesets(self): s = RRuleSchedule( rrule="DTSTART:19970902T090000\n" From 859ccab174047346115f7da7f46b6c36c33d50e3 Mon Sep 17 00:00:00 2001 From: Jake Kaplan Date: Mon, 5 Dec 2022 11:15:11 -0500 Subject: [PATCH 2/6] add docs --- docs/concepts/schedules.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/concepts/schedules.md b/docs/concepts/schedules.md index 69bcf29119ea..771e749e5695 100644 --- a/docs/concepts/schedules.md +++ b/docs/concepts/schedules.md @@ -178,6 +178,9 @@ schedule: rrule: 'FREQ=WEEKLY;BYDAY=MO,WE,FR;UNTIL=20240730T040000Z' ``` +!!! info "Max RRule length" + Note the max supported character length of an `rrulestr` is 6500 characters + !!! info "Daylight saving time considerations" Note that as a calendar-oriented standard, `RRules` are sensitive to the initial timezone provided. A 9am daily schedule with a DST-aware start date will maintain a local 9am time through DST boundaries. A 9am daily schedule with a UTC start date will maintain a 9am UTC time. From eaecd048ce56aa47d83cdcb0d040e7406a64489a Mon Sep 17 00:00:00 2001 From: Jake Kaplan Date: Mon, 5 Dec 2022 12:58:24 -0500 Subject: [PATCH 3/6] increase max length -> 32000 --- docs/concepts/schedules.md | 2 +- src/prefect/orion/schemas/schedules.py | 4 ++-- tests/orion/schemas/test_schedules.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/concepts/schedules.md b/docs/concepts/schedules.md index 771e749e5695..d179e82f8b05 100644 --- a/docs/concepts/schedules.md +++ b/docs/concepts/schedules.md @@ -179,7 +179,7 @@ schedule: ``` !!! info "Max RRule length" - Note the max supported character length of an `rrulestr` is 6500 characters + Note the max supported character length of an `rrulestr` is 32000 characters !!! info "Daylight saving time considerations" Note that as a calendar-oriented standard, `RRules` are sensitive to the initial timezone provided. A 9am daily schedule with a DST-aware start date will maintain a local 9am time through DST boundaries. A 9am daily schedule with a UTC start date will maintain a 9am UTC time. diff --git a/src/prefect/orion/schemas/schedules.py b/src/prefect/orion/schemas/schedules.py index 7437cf529b66..635d2f257b5a 100644 --- a/src/prefect/orion/schemas/schedules.py +++ b/src/prefect/orion/schemas/schedules.py @@ -15,8 +15,8 @@ from prefect.orion.utilities.schemas import DateTimeTZ, PrefectBaseModel MAX_ITERATIONS = 1000 -# approx. a years worth of RDATEs + buffer -MAX_RRULE_LENGTH = 6500 +# approx. 5 years worth of RDATEs + buffer +MAX_RRULE_LENGTH = 32000 def _prepare_scheduling_start_and_end( diff --git a/tests/orion/schemas/test_schedules.py b/tests/orion/schemas/test_schedules.py index c368bf4df7be..d47e4e0cee3c 100644 --- a/tests/orion/schemas/test_schedules.py +++ b/tests/orion/schemas/test_schedules.py @@ -651,7 +651,7 @@ async def test_rrule_validates_rrule_str(self): async def test_rrule_max_rrule_len(self): start = datetime(2000, 1, 1) s = "RDATE:" + ",".join( - [start.add(days=i).format("YMMDD") + "T000000Z" for i in range(1000)] + [start.add(days=i).format("YMMDD") + "T000000Z" for i in range(3000)] ) assert len(s) > MAX_RRULE_LENGTH with pytest.raises(ValidationError, match="Max length"): From 0df74e2a908ef2daa2944c1d447d6067541f6e6f Mon Sep 17 00:00:00 2001 From: Jake Kaplan Date: Mon, 12 Dec 2022 10:35:07 -0500 Subject: [PATCH 4/6] restore 6500 rrule limit --- docs/concepts/schedules.md | 2 +- src/prefect/orion/schemas/schedules.py | 4 ++-- tests/orion/schemas/test_schedules.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/concepts/schedules.md b/docs/concepts/schedules.md index d179e82f8b05..771e749e5695 100644 --- a/docs/concepts/schedules.md +++ b/docs/concepts/schedules.md @@ -179,7 +179,7 @@ schedule: ``` !!! info "Max RRule length" - Note the max supported character length of an `rrulestr` is 32000 characters + Note the max supported character length of an `rrulestr` is 6500 characters !!! info "Daylight saving time considerations" Note that as a calendar-oriented standard, `RRules` are sensitive to the initial timezone provided. A 9am daily schedule with a DST-aware start date will maintain a local 9am time through DST boundaries. A 9am daily schedule with a UTC start date will maintain a 9am UTC time. diff --git a/src/prefect/orion/schemas/schedules.py b/src/prefect/orion/schemas/schedules.py index 635d2f257b5a..3daad385446a 100644 --- a/src/prefect/orion/schemas/schedules.py +++ b/src/prefect/orion/schemas/schedules.py @@ -15,8 +15,8 @@ from prefect.orion.utilities.schemas import DateTimeTZ, PrefectBaseModel MAX_ITERATIONS = 1000 -# approx. 5 years worth of RDATEs + buffer -MAX_RRULE_LENGTH = 32000 +# approx. 1 years worth of RDATEs + buffer +MAX_RRULE_LENGTH = 6500 def _prepare_scheduling_start_and_end( diff --git a/tests/orion/schemas/test_schedules.py b/tests/orion/schemas/test_schedules.py index d47e4e0cee3c..c36292096a1d 100644 --- a/tests/orion/schemas/test_schedules.py +++ b/tests/orion/schemas/test_schedules.py @@ -651,7 +651,7 @@ async def test_rrule_validates_rrule_str(self): async def test_rrule_max_rrule_len(self): start = datetime(2000, 1, 1) s = "RDATE:" + ",".join( - [start.add(days=i).format("YMMDD") + "T000000Z" for i in range(3000)] + [start.add(days=i).format("YMMDD") + "T000000Z" for i in range(365 * 3)] ) assert len(s) > MAX_RRULE_LENGTH with pytest.raises(ValidationError, match="Max length"): From fd27cc153603471fb5eed0f1521e5652efafeff0 Mon Sep 17 00:00:00 2001 From: Jake Kaplan Date: Fri, 6 Jan 2023 15:17:37 -0500 Subject: [PATCH 5/6] truncate original rrule in error msg --- src/prefect/orion/schemas/schedules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/prefect/orion/schemas/schedules.py b/src/prefect/orion/schemas/schedules.py index 3daad385446a..2fa3b04bd2ba 100644 --- a/src/prefect/orion/schemas/schedules.py +++ b/src/prefect/orion/schemas/schedules.py @@ -398,7 +398,7 @@ def validate_rrule_str(cls, v): raise ValueError(f'Invalid RRule string "{v}": {exc}') if len(v) > MAX_RRULE_LENGTH: raise ValueError( - f'Invalid RRule string "{v}"\n' + f'Invalid RRule string "{v[:40] + "..." if len(v) > 40 else v}"\n' f"Max length is {MAX_RRULE_LENGTH}, got {len(v)}" ) return v From 4de6aea7f10b225b9e634fed7de02e1c8de54f68 Mon Sep 17 00:00:00 2001 From: Jake Kaplan Date: Fri, 6 Jan 2023 15:35:17 -0500 Subject: [PATCH 6/6] update rrule length error message --- src/prefect/orion/schemas/schedules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/prefect/orion/schemas/schedules.py b/src/prefect/orion/schemas/schedules.py index 2fa3b04bd2ba..8a22bb820970 100644 --- a/src/prefect/orion/schemas/schedules.py +++ b/src/prefect/orion/schemas/schedules.py @@ -398,7 +398,7 @@ def validate_rrule_str(cls, v): raise ValueError(f'Invalid RRule string "{v}": {exc}') if len(v) > MAX_RRULE_LENGTH: raise ValueError( - f'Invalid RRule string "{v[:40] + "..." if len(v) > 40 else v}"\n' + f'Invalid RRule string "{v[:40]}..."\n' f"Max length is {MAX_RRULE_LENGTH}, got {len(v)}" ) return v