镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

Extend startup snapshot support - #8405

Open
dbezhetskov wants to merge 4 commits into
cloudflare:mainfrom
dbezhetskov:live-snapshots-4
Open

dbezhetskov wants to merge 4 commits into
cloudflare:mainfrom
dbezhetskov:live-snapshots-4

Conversation

@dbezhetskov

Copy link
Copy Markdown
Contributor

Support for the snapshot:

  • new module registry
  • C++ streams
  • C++ Performance cloning

…napshot

A Worker on the new module registry (`new_module_registry`) now qualifies for
the startup snapshot pipeline. The zygote shares the Worker's
`jsg::modules::ModuleRegistry`: the registry itself holds no V8 state, every
V8 handle lives in the per-context `IsolateModuleRegistry`, so the Worker
started from the snapshot re-resolves the baked entries in that same registry.
The startup snapshot no longer requires the TypeScript streams
implementation. What kept C++ streams out was a stream whose pull() or start()
is still pending when top-level evaluation ends: its C++ promise continuation
is a v8::Function whose callback is a template instantiation the zygote's
external reference table does not hold, so CreateBlob() aborted the process on
the unknown address.

The jsg::Promise::thenImpl now registers the two callbacks of
every continuation created while a snapshot is being prepared; the serializer
then reaches the continuation's opaque callback data, which has no snapshot
transfer, and the zygote fails with the usual unclonable report
(ReadableStreamDefaultController, OpaqueWrappable, ReadableStream), so the
Worker starts without a snapshot instead of taking the process down. A C++
stream whose top level leaves no pending pull transfers like any other object.
The `performance` global is reachable from the context of any Worker whose top
level touches it (reading performance.now() or importing node:perf_hooks is
enough), and Performance had no snapshot transfer, so such Workers started
without a snapshot. An instance that has recorded no entries is rebuilt from
the isolate's limit enforcer; one with entries holds jsg::Refs to
PerformanceEntry objects, which a clone cannot carry, and stays unclonable.
@dbezhetskov
dbezhetskov requested review from a team as code owners November 12, 2026 17:24
@clanky-the-clanker

clanky-the-clanker Bot commented Nov 12, 2026 •

Copy link
Copy Markdown

This pull request's reviews are posted as pull request reviews: see the review of 2e74745f.

@csjh

csjh commented Nov 12, 2026

Copy link
Copy Markdown
Contributor

clanky review

@clanky-the-clanker clanky-the-clanker Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💬 Approved with comments — One WARNING: Performance::snapshotClone() silently drops top-level EventTarget listeners on performance. Plus an INFO about missing tests for the new Performance clone and the pending-C++-continuation fallback.

Findings: 1 warning · 1 info (2 are inline comments on the diff)

The decision is advisory: this review is a comment and never approves, blocks or requests changes on the pull request.

🔧 Fix with your agent

Paste the following into your coding agent to investigate and fix these issues.

Fix the following code review issues found in pull request #8405.

Before making changes, review each issue and present a brief summary table:
- For each finding, state whether you agree, disagree, or need clarification
- If you disagree (e.g., the fix would require disproportionate changes
  for minimal ROI, or the finding is factually incorrect), explain why
- If you need to clarify something before deciding, ask those questions
- Then share your plan for which issues to tackle and in what order

## WARNING (1)

### [Code Quality] `Performance::snapshotClone()` checks only `entries`, so listeners added to `performance` at the top level are silently dropped when the Worker starts from the snapshot.
- **File:** src/workerd/api/performance.c++:16

## INFO (1)

### [Code Quality] No test covers the pending-C++-continuation fallback registered here, or `Performance::snapshotClone()`.
- **File:** src/workerd/jsg/promise.h:457

📋 Code Quality: ⚠️ 1 warning · 1 info

Performance clone loses EventTarget listeners; the new Performance clone and pending-continuation fallback have no tests.

Recommendation: 📝 Minor Comments

  • [WARNING] Performance::snapshotClone() checks only entries, so listeners added to performance at the top level are silently dropped when the Worker starts from the snapshot. (src/workerd/api/performance.c++:16)
  • [INFO] No test covers the pending-C++-continuation fallback registered here, or Performance::snapshotClone(). (src/workerd/jsg/promise.h:457)
View full review
  • src/workerd/api/performance.c++:16: Performance extends EventTarget. When enable_global_performance_classes or the perf_hooks flag is on, it inherits addEventListener and on* handlers (performance.h:603-604), and those handlers live in EventTarget::typeMap. snapshotClone() only checks entries and returns a fresh Performance with an empty handler map. The snapshot restores listeners in C++ only for the global scope (EventTarget::restoreSnapshotListener()). A listener registered on performance at the top level is therefore lost on load, and the Worker does not fall back to starting without a snapshot.
  • src/workerd/jsg/promise.h:457: nothing tests the new isPreparingSnapshot() registration in thenImpl. The cpp-streams case deliberately has no pending pull(). Nothing tests Performance::snapshotClone() either, neither the clonable case nor the fallback once entries are recorded.

✅ All other checks passed

🔒 Security: ✅ LGTM

No security issues found in this change.


📖 AGENTS.md: ✅ LGTM

This pull request does not change any AGENTS.md files.


⚡ Performance: ✅ LGTM

No performance issues found.


📝 Documentation: ✅ LGTM

The updated docs/startup-snapshot.md sections match the changed code.


Reviewed commit: 2e74745f

namespace workerd::api {

kj::Maybe<kj::Own<jsg::Wrappable>> Performance::snapshotClone() const {
if (entries.size() != 0) return kj::none;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ WARNING · Code Quality

Performance::snapshotClone() checks only entries, so listeners added to performance at the top level are silently dropped when the Worker starts from the snapshot.

Performance is an EventTarget. Under the global-performance-classes or perf_hooks flags, a Worker can call performance.addEventListener(...) or set an on* handler at the top level, and those handlers live in EventTarget::typeMap. This clone only refuses when entries is non-empty, so the restored Worker gets a Performance with no listeners. A later performance.dispatchEvent(...) then does nothing, and nothing falls back to a plain start. Return kj::none when the EventTarget has any registered handlers too, which probably needs a small accessor on EventTarget.

Comment thread src/workerd/jsg/promise.h
return js.withinHandleScope([&] {
auto context = js.v8Context();

if (js.isPreparingSnapshot()) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ INFO · Code Quality

No test covers the pending-C++-continuation fallback registered here, or Performance::snapshotClone().

This registration is now the only thing that turns a pending C++ continuation (such as a stream whose pull() is still pending at the end of top-level evaluation) into a failed zygote instead of a V8 abort. The worker factory no longer blocks this configuration up front. A .wd-test case with such a stream should assert that the snapshot is not used and that the Worker still serves requests. Cases for performance.mark() at the top level and for a listener on performance would also cover the new Performance clone.

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