DSH / Atlas
2026-09-24implementedfeaturearchived 2026-09-30

One-minute fixed-rate floor

固定速率下限改为 1 分钟

`every_seconds` refused every interval below 300 seconds, and the Web client restated that bound as its own per-unit floors of 1 hour, 5 minutes, or 300 seconds. The same product already offered a `cron` selector whose finest granularity is one minute, so two selectors that both describe a repeating cadence disagreed about the shortest one a person could ask for: a reminder that repeats every minute was expressible a

English

Problem

every_seconds refused every interval below 300 seconds, and the Web client restated that bound as its own per-unit floors of 1 hour, 5 minutes, or 300 seconds. The same product already offered a cron selector whose finest granularity is one minute, so two selectors that both describe a repeating cadence disagreed about the shortest one a person could ask for: a reminder that repeats every minute was expressible as cron and rejected as every_seconds. The floor also made a shipped default Web surface awkward to try, because the first repeating reminder anyone creates cannot come back in under five minutes.

Decision

MIN_EVERY_INTERVAL_SECONDS in packages/schedule/schedule/src/domain.ts is 60. The Host decoder, the create and update validation, the every_seconds tool parameter description, and its refusal message all read that constant, so one change moves the bound everywhere the Host states it.

The client keeps its own MIN_INTERVAL_SECONDS = 60, which the Run time card uses for the seconds row and to derive the per-unit floors: 1 hour, 1 minute, or 60 seconds. The localized hint and refusal copy states 1 minute and 60 seconds for those rows.

One minute is the shortest cadence that does not need a new protocol decision: it matches the cron selector's granularity, and every accepted value remains a whole-second interval the durable record already stores. A stored record keeps whatever the Host accepted when it was created; a record written under the old bound (300 seconds or more) stays valid and replayable, and no migration runs because the previous bound rejected anything below it.

Alternatives considered

Keep the 300-second floor. The two selectors would keep disagreeing about the shortest repeating cadence, and a person could still reach a one-minute cadence only through cron.

Lower the bound to one second. Sub-minute intervals multiply durable follow-up messages and timer re-anchoring per reminder without a request behind them; the minute granularity already covers the shortest cadence the product names elsewhere.

Consequences

  • A fixed-rate reminder may now repeat every minute; the smallest accepted value is model-visible in the every_seconds description and in the client's hint copy.
  • Delivery behavior, batching, catch-up, and the interval arithmetic are unchanged: an accepted interval still anchors to creation or to the accepted save, and each overdue record still contributes only its latest occurrence.
  • A deployment cannot raise the floor through configuration; the bound is a protocol constant, so a deployment that needs a longer minimum enforces it in the interface that creates tasks.

中文

Problem

every_seconds 会拒绝所有低于 300 秒的间隔,Web 客户端也把该下限复述为自己按单位计算的下限:1 小时、5 分钟或 300 秒。同一产品已经提供粒度为 1 分钟的 cron 选择器,于是两个都在描述重复节奏的选择器对「最短能填多少」给出了不同答案:每分钟重复一次的提醒用 cron 可以表达,用 every_seconds 会被拒绝。该下限也让默认发布 Schedule 的 Web 界面难以试用——任何人创建的第一条重复提醒都不可能在五分钟内回到。

Decision

packages/schedule/schedule/src/domain.ts 的 MIN_EVERY_INTERVAL_SECONDS 为 60。宿主解码器、创建与更新校验、every_seconds 工具参数描述及其拒绝消息都读取该常量,因此一处改动即可移动宿主陈述该下限的所有位置。

客户端保留自己的 MIN_INTERVAL_SECONDS = 60,「运行时间」卡片用它校验秒数行,并据此推导各单位的数量下限:1 小时、1 分钟或 60 秒。对应行的本地化提示与拒绝文案改为 1 分钟与 60 秒。

1 分钟是无需新增协议决策的最短节奏:它与 cron 选择器的粒度一致,且每个被接受的值仍是以整秒表示的间隔,持久记录本来就以整秒存储。已存储的记录保留宿主当初接受的值:按旧下限(300 秒及以上)写入的记录仍然有效、仍可回放;由于旧下限拒绝了低于它的值,因此不存在需要迁移的数据。

Alternatives considered

保留下限 300 秒。 两个选择器会继续对最短重复节奏给出不同答案,而每分钟一次的节奏仍只能通过 cron 达到。

把下限降到 1 秒。 低于分钟级的间隔会让每条提醒产生更多持久 follow-up 消息与 timer 重新对齐,而背后没有这样的需求;分钟粒度已经覆盖产品在别处命名的最短节奏。

Consequences

  • 固定速率提醒现在可以每分钟重复一次;最小可接受值对模型可见,出现在 every_seconds 的描述与客户端的提示文案中。
  • 投递行为、批处理、补跑与间隔运算法则不变:被接受的间隔仍以创建时刻或接受保存的时刻为起算点,每条逾期记录仍只贡献最新一次发生时点。
  • 部署无法通过配置提高该下限;它是协议常量,需要更长最小间隔的部署应在创建任务的界面上自行约束。