Skip to content

docs: timers guide - #9

Merged
marc0olo merged 4 commits into
mainfrom
docs/guides-backends-timers
Mar 16, 2026
Merged

marc0olo merged 4 commits into
mainfrom
docs/guides-backends-timers

Conversation

@raymondk

@raymondk raymondk commented Mar 13, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

  • Covers timer APIs for Rust (ic-cdk-timers v1.0.0) and Motoko (mo:core/Timer v2.1.0): one-shot, recurring, serial, and cancellation
  • System time API (ic_cdk::api::time() / Time.now()) with per-message consistency guarantee
  • Common patterns: periodic cleanup, data aggregation, timed state transitions
  • Upgrade handling: timers cleared on upgrade, re-register in post_upgrade/postupgrade
  • Cycle costs, idempotency requirements, CDK rate limits (250 global, 5 per timer)
  • Heartbeats (legacy) with migration guide
  • FAQ: DTS support, await behavior, overlapping execution
  • Limitations: relative time only, security warning, resolution similar to block rate
  • Time conversion packages (Motoko mops, Rust crates)

All Rust snippets verified against ic-cdk-timers v1.0.0 (set_timer takes futures, set_timer_interval takes FnMut() -> Future). All Motoko snippets verified against motoko-core v2.1.0 and compiler v1.3.0.

Sync recommendation

Informed by dfinity/portal periodic-tasks-timers.mdx + time-and-timestamps.mdx, dfinity/cdk-rs ic-cdk-timers/src/lib.rs, and caffeinelabs/motoko-core src/Timer.mo

@marc0olo

Copy link
Copy Markdown
Contributor

Review: Timers

Must fix

  • Missing content brief topics — "scheduled data aggregation" and "timed state transitions": The content brief specifically asks for "Common patterns: periodic cleanup, scheduled data aggregation, timed state transitions." The page only mentions periodic cleanup in a code comment but doesn't show any of these patterns as concrete examples. At minimum, a "Common patterns" section with brief descriptions and skeleton snippets (or links to examples) would satisfy the brief.

  • Portal FAQ content omitted: The portal source (periodic-tasks-timers.mdx:112-119) has three useful FAQs that developers will search for:

    1. Do timers support deterministic time slicing (DTS)? (Yes)
    2. What happens if a timer handler awaits a call? (Normal await rules apply)
    3. What happens if a 1s periodic timer executes for 2s?

    These are exactly the "hard parts" a developer would struggle with. They should be included (or at least the await-point behavior, which is a common footgun).

  • Motoko postupgrade claim needs verification: The page says "read it from stable variables in postupgrade" and uses system func postupgrade(). The portal source and the mo:base/Timer examples use canister_post_upgrade semantics. Since the page targets mo:core (the modern library), postupgrade may be correct for persistent actors — but this should be verified or flagged with <!-- Needs human verification --> since there's no .sources/ reference confirming this exact spelling/syntax for persistent actors.

Suggestions

  • "A duration of 0 in Motoko will only fire once, not repeatedly" — This claim appears in the "Recurring timers" section but has no source in .sources/. If true, it's useful; if not, it's misleading. Either verify it or flag it with <!-- Needs human verification -->.

  • Heartbeat interval: The page says "~1 second" but the portal says "intervals close to the blockchain finalization rate (1s)". Consider matching the portal's phrasing since the actual interval is implementation-defined and not guaranteed to be 1s.

  • Time conversion section missing: The writing note says "Also cover ic0.time() behavior and timestamps (portal: time-and-timestamps.mdx) — developers looking for 'how does time work on ICP' will land here." The "System time" section covers ic_cdk::api::time() and Time.now(), which is good. But the portal's timestamp page also covers DateTime conversions and conversion packages (time crate, mops.one/time, mops.one/datetime). Consider at least mentioning these exist and linking to them, since this page is where "how does time work" developers will land.

  • "Advanced scheduling" limitation omitted: The portal mentions that CDK timers use relative time only — absolute scheduling requires manual calculation or a third-party library. This is a practical limitation worth including.

  • Funnel check: The page ends with a link to the Rust periodic tasks example, which is fine, but there's no "what's next" guidance. A brief closing like "For canister lifecycle hooks referenced above, see Canister lifecycle" would help readers continue.

Verified

  • All internal links resolve: ../../concepts/timers.md, ../canister-management/lifecycle.md, ../../reference/cycles-costs.md all exist
  • External URLs are correct: github.com/dfinity/examples/tree/master/rust/periodic_tasks matches .sources/examples, learn.internetcomputer.org is an allowed external link
  • No dfx references — clean
  • No .mdx/JSX — plain markdown throughout
  • Motoko uses mo:core/ (not mo:base/) per content rules
  • Rust code patterns (ic_cdk_timers::set_timer, set_timer_interval, clear_timer, ic_cdk::spawn for async) verified against .sources/examples/rust/periodic_tasks/timer/src/lib.rs
  • Frontmatter is complete and consistent with body content
  • Sync recommendation present: <!-- Upstream: informed by dfinity/portal ... -->
  • Code snippets are all under 30 lines inline

raymondk and others added 3 commits March 16, 2026 14:41
Review feedback (marc0olo):
- Add "Common patterns" section (cleanup, data aggregation, state transitions)
- Add FAQ section from portal (DTS, await behavior, overlapping timers)
- Fix heartbeat interval wording to match portal ("close to finalization rate")
- Add time conversion packages (Motoko mops, Rust crates)
- Add limitations section (relative time only, security warning, resolution)
- Add "What's next" links to lifecycle, concepts, and costs pages

Source-verified fixes (from .sources/cdk-rs and .sources/motoko-core):
- Update all Rust snippets to ic-cdk-timers v1.0.0 API: set_timer takes
  a future (async {}), set_timer_interval takes FnMut() -> Future (|| async {})
- Remove obsolete ic_cdk::spawn pattern (no longer needed)
- Add set_timer_interval_serial for state-mutating periodic tasks
- Add idempotency warning and CDK rate limits (250 global, 5 per timer)
- Verify Motoko postupgrade spelling and duration 0 behavior against source
The previous wording claimed system time is the same for all messages
in a round. The actual guarantee from motoko-core Time.mo:42 is that
time is constant within a single message execution only.
@marc0olo
marc0olo force-pushed the docs/guides-backends-timers branch from 1e7a6e5 to 21bf406 Compare March 16, 2026 13:47
@marc0olo

Copy link
Copy Markdown
Contributor

Feedback addressed:

From review:

  • Added "Common patterns" section (periodic cleanup, data aggregation, timed state transitions, heartbeat migration)
  • Added FAQ section from portal (DTS support, await behavior, overlapping timer execution)
  • Fixed heartbeat interval wording ("close to blockchain finalization rate")
  • Added time conversion packages (Motoko mops, Rust crates)
  • Added "Limitations" section (relative time only, security warning, timer resolution)
  • Added "What's next" links (lifecycle, concepts, costs)

Source-verified fixes (from new .sources/cdk-rs and .sources/motoko-core submodules):

  • Updated all Rust snippets to ic-cdk-timers v1.0.0 API: set_timer takes a future (async {}), set_timer_interval takes FnMut() -> Future (|| async {})
  • Removed obsolete ic_cdk::spawn pattern
  • Added set_timer_interval_serial for state-mutating periodic tasks
  • Added idempotency warning and CDK rate limits (250 global, 5 per interval timer)
  • Corrected system time guarantee (constant per message execution, not per round)
  • Verified postupgrade spelling and duration 0 behavior against compiler/library source

Verification status: All 20 technical claims verified against .sources/ (cdk-rs, motoko-core, motoko compiler, portal).

@marc0olo
marc0olo merged commit 2d48373 into main Mar 16, 2026
1 check passed
@marc0olo
marc0olo deleted the docs/guides-backends-timers branch March 16, 2026 14:23
marc0olo pushed a commit that referenced this pull request Sep 9, 2026
Bumps [svgo](https://github.com/svg/svgo) from 4.0.2 to 4.1.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/svg/svgo/releases">svgo's
releases</a>.</em></p>
<blockquote>
<h2>v4.1.0</h2>
<p>This minor release upgrades the SAX parser and introduces stricter
XML validation. It also includes important security hardening for
<code>removeScripts</code>, dependency updates, and improvements to the
test and regression infrastructure.</p>
<h3>Support SVGO</h3>
<p>If SVGO is valuable to you or your organization, please consider <a
href="https://opencollective.com/svgo">supporting the project on
OpenCollective</a>. Your sponsorship helps fund ongoing maintenance and
security work.</p>
<h3>Stricter XML validation</h3>
<p>SVGO now uses <a
href="https://www.npmjs.com/package/sax"><code>sax</code> 1.6.1</a>,
upgraded from 1.5.0 (<a
href="https://redirect.github.com/svg/svgo/pull/2257">#2257</a>).</p>
<p>The new parser version validates numeric character references against
the ranges permitted by XML. Invalid references are now rejected in both
text and attributes, including:</p>
<ul>
<li>disallowed control characters such as
<code>&amp;[#1](https://github.com/svg/svgo/issues/1);</code>,
<code>&amp;#xB;</code>, and <code>&amp;#x1F;</code>;</li>
<li>UTF-16 surrogate code points such as <code>&amp;#xD800;</code>;</li>
<li>invalid XML code points such as <code>&amp;#xFFFF;</code>.</li>
</ul>
<p>Valid boundary values—including <code>U+0020</code>,
<code>U+D7FF</code>, <code>U+E000</code>, <code>U+FFFD</code>, and
characters through <code>U+10FFFF</code>—remain supported.</p>
<p>Parser failures are consistently exposed as
<code>SvgoParserError</code> errors with an <code>Invalid character
entity</code> reason.</p>
<p>This is an intentional behavior change: malformed SVGs that were
previously accepted may now produce a parser error, while valid XML
documents are unaffected.</p>
<h3>Security</h3>
<p>The <a
href="https://svgo.dev/docs/plugins/removeScripts/"><code>removeScripts</code></a>
plugin has been hardened against several script-execution bypasses:</p>
<ul>
<li>Filters executable <code>data:</code> URLs containing HTML, XHTML,
or SVG documents while preserving inert data such as PNG images, and
filters legacy <code>vbscript:</code> URLs (<a
href="https://redirect.github.com/svg/svgo/pull/2263">#2263</a>).</li>
<li>Sanitizes content inside SVG <code>&lt;foreignObject&gt;</code>
elements by removing HTML event-handler attributes, <code>srcdoc</code>,
and executable URLs from <code>action</code>, <code>data</code>,
<code>formaction</code>, <code>href</code>, and <code>src</code>, while
preserving non-executable HTML and visual content (<a
href="https://redirect.github.com/svg/svgo/pull/2264">#2264</a>).</li>
<li>Recognizes namespace-prefixed SVG <code>&lt;a&gt;</code> elements
and removes ASCII tabs and newlines before checking URL schemes,
preventing values such as
<code>java&amp;[#9](https://github.com/svg/svgo/issues/9);script:</code>
from bypassing detection while preserving elements in unrelated custom
namespaces (<a
href="https://redirect.github.com/svg/svgo/pull/2268">#2268</a>).</li>
</ul>
<p>These changes address:</p>
<ul>
<li><a
href="https://github.com/svg/svgo/security/advisories/GHSA-4vpr-x523-8j87">GHSA-4vpr-x523-8j87</a></li>
<li><a
href="https://github.com/svg/svgo/security/advisories/GHSA-w27v-7q3p-w38r">GHSA-w27v-7q3p-w38r</a></li>
</ul>
<h3>Dependencies</h3>
<ul>
<li>Upgraded <code>css-select</code> to v6 and <code>css-what</code> to
v7, and updated SVGO's custom selector adapter for
<code>css-select</code> v6 (<a
href="https://redirect.github.com/svg/svgo/pull/2244">#2244</a>).</li>
</ul>
<h3>Project maintenance</h3>
<p><a href="https://github.com/TrySound"><code>@​TrySound</code></a> is
back as an active SVGO maintainer.</p>
<p>Many thanks to <a
href="https://github.com/KTibow"><code>@​KTibow</code></a>, <a
href="https://github.com/SethFalco"><code>@​SethFalco</code></a>, and <a
href="https://github.com/XhmikosR"><code>@​XhmikosR</code></a> for
maintaining and improving SVGO over the past several years.</p>
<p><strong>Full Changelog:</strong> <a
href="https://github.com/svg/svgo/compare/v4.0.2...v4.1.0">https://github.com/svg/svgo/compare/v4.0.2...v4.1.0</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/svg/svgo/commit/5765cbe4e0a930dca648c6b81d335b1522cd7375"><code>5765cbe</code></a>
chore: prepare v4.1.0 release (<a
href="https://redirect.github.com/svg/svgo/issues/2275">#2275</a>)</li>
<li><a
href="https://github.com/svg/svgo/commit/3db3ef33e409a0bc0fdaf255e46c908b00e93bc2"><code>3db3ef3</code></a>
fix(removeScripts): handle anchor URL bypasses (<a
href="https://redirect.github.com/svg/svgo/issues/2268">#2268</a>)</li>
<li><a
href="https://github.com/svg/svgo/commit/4e9b9aed2a4607cf28025871151421d5bfe86484"><code>4e9b9ae</code></a>
chore: cache regression screenshots (<a
href="https://redirect.github.com/svg/svgo/issues/2267">#2267</a>)</li>
<li><a
href="https://github.com/svg/svgo/commit/d55270ce17b99ebfe16e4fad028bd162187205a1"><code>d55270c</code></a>
chore(regression): migrate comparison workers to Tinypool (<a
href="https://redirect.github.com/svg/svgo/issues/2266">#2266</a>)</li>
<li><a
href="https://github.com/svg/svgo/commit/fd51e474a300417d9361d9302d596b1763146327"><code>fd51e47</code></a>
fix(removeScripts): sanitize foreignObject content (<a
href="https://redirect.github.com/svg/svgo/issues/2264">#2264</a>)</li>
<li><a
href="https://github.com/svg/svgo/commit/dcaf957c6eb34832844de11ef2792e3e0e8db5dd"><code>dcaf957</code></a>
chore: optimize fixtures in a bounded worker pool (<a
href="https://redirect.github.com/svg/svgo/issues/2265">#2265</a>)</li>
<li><a
href="https://github.com/svg/svgo/commit/a3542937d9c85debab04b1ff75207768caf8a058"><code>a354293</code></a>
fix(removeScripts): filter executable data URLs (<a
href="https://redirect.github.com/svg/svgo/issues/2263">#2263</a>)</li>
<li><a
href="https://github.com/svg/svgo/commit/4e0d2ac5e7abd5f12d650d3e4b4d2500d4944cc5"><code>4e0d2ac</code></a>
ci(typecheck): return typechecking on CI</li>
<li><a
href="https://github.com/svg/svgo/commit/0b97fedd00a3001f1da616f61578c2c12e6cde1f"><code>0b97fed</code></a>
test(typescript): drop tsd for vitest type testing API</li>
<li><a
href="https://github.com/svg/svgo/commit/f6e8ae1afb012c2b47675622728d76b9bfd734f5"><code>f6e8ae1</code></a>
chore(pnpm): drop package.json#pnpm.onlyBuiltDependencies</li>
<li>Additional commits viewable in <a
href="https://github.com/svg/svgo/compare/v4.0.2...v4.1.0">compare
view</a></li>
</ul>
</details>
<details>
<summary>Maintainer changes</summary>
<p>This version was pushed to npm by <a
href="https://www.npmjs.com/~GitHub%20Actions">GitHub Actions</a>, a new
releaser for svgo since your current version.</p>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=svgo&package-manager=npm_and_yarn&previous-version=4.0.2&new-version=4.1.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/dfinity/developer-docs/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants