Skip to content

Refactor console method decoration for snapshotting - #6874

Open
dbezhetskov wants to merge 1 commit into
cloudflare:mainfrom
dbezhetskov:live-snapshots-1
Open

Refactor console method decoration for snapshotting#6874
dbezhetskov wants to merge 1 commit into
cloudflare:mainfrom
dbezhetskov:live-snapshots-1

Conversation

@dbezhetskov

Copy link
Copy Markdown

The current console decoration scheme breaks V8 snapshot creation in two ways:

  1. The C++ lambdas used as decorators hold v8::Global handles to the
    original console methods, but V8 does not support serializing Global
    handles.

  2. The decorator for each console method is created through
    wrapSimpleFunction, which allocates an opaque JS wrapper using the
    Wrappable path and binds that wrapper to the resulting V8 function
    via V8::Function::New. The JS wrapper, which holds the C++ lambda,
    belongs to the context. However, V8 snapshot forbids pointers from
    isolate-level snapshot objects to context-level data. In this case,
    the function/template machinery belongs to the isolate-level snapshot,
    while the wrapper belongs to the context-level snapshot.

This patch addresses both issues:

  • We preserve the original methods in a private, context-level embedder-data slots instead of holding them in v8::Global handles. This establishes edges from the context to those JS objects, so they remain reachable and are automatically captured in the snapshot as part of the context graph.

  • We no longer use the Wrappable path for decoration. Instead, we create the V8 functions directly with V8::Function::New and use a plain C++ functor that retrieves the required state from the context-level embedder-data slot. This avoids creating JS wrappers that hold references to C++ state.

@dbezhetskov
dbezhetskov requested review from a team as code owners July 10, 2026 10:34
@github-actions

github-actions Bot commented Jul 10, 2026

Copy link
Copy Markdown

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

@dbezhetskov

Copy link
Copy Markdown
Author

I have read the CLA Document and I hereby sign the CLA

github-actions Bot added a commit that referenced this pull request Jul 10, 2026
@dbezhetskov
dbezhetskov force-pushed the live-snapshots-1 branch 2 times, most recently from a2bf6f9 to e33db21 Compare July 10, 2026 13:32
@codspeed-hq

codspeed-hq Bot commented Aug 14, 2026

Copy link
Copy Markdown

Merging this PR will regress 1 benchmark

⚠️ Different runtime environments detected

Some benchmarks with significant performance changes were compared across different runtime environments,
which may affect the accuracy of the results.

Open the report in CodSpeed to investigate

⚡ 1 improved benchmark
❌ 1 regressed benchmark
✅ 70 untouched benchmarks
⏩ 129 skipped benchmarks1

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Benchmark BASE HEAD Efficiency
request[RegExpBenchmark] 4.1 ms 4.5 ms -8.95%
bm_Promise_Fib10 20.1 µs 18.3 µs +10.22%

Tip

Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.


Comparing dbezhetskov:live-snapshots-1 (3c8e057) with main (cd1f881)

Open in CodSpeed

Footnotes

  1. 129 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

Comment thread src/workerd/io/worker.c++ Outdated
{"error", LogLevel::ERROR, &consoleDecorator<1>},
{"info", LogLevel::INFO, &consoleDecorator<2>},
{"log", LogLevel::LOG, &consoleDecorator<3>},
{"warn", LogLevel::WARN, &consoleDecorator<4>},

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Simply because it's a bit opaque here, either making the numbers here named constants or adding some comments here would be useful.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

done

// V2 (Local<Data>) embedder data slots. Read/written via
// SetEmbedderDataV2/GetEmbedderDataV2 — distinct API from the aligned-pointer
// slots above. Indices start past MAX_POINTER_SLOT so the two slot kinds never
// collide in the underlying embedder-data array.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Just so it's clear, some guidance about what belongs in the aligned-pointer slots vs. the context data slots here would be helpful (like a single quick sentence)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

done

Comment thread src/workerd/io/worker.c++ Outdated
// Save original methods into context so that GC can track them (so they are captured in a
// snapshot) and we can find them at a known location from the decorator.
for (size_t i = 0; i < kConsoleMethodsCount; ++i) {
auto methodStr = jsg::v8StrIntern(lock.v8Isolate, kConsoleMethods[i].name);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

lock.strIntern(...) ... slightly less verbose

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

done

Comment thread src/workerd/io/worker.c++ Outdated
auto methodStr = jsg::v8StrIntern(lock.v8Isolate, kConsoleMethods[i].name);
auto original = jsg::check(console->Get(context, methodStr)).As<v8::Function>();
auto slot = static_cast<jsg::ContextDataSlot>(
static_cast<int>(jsg::ContextDataSlot::CONSOLE_ORIGINAL_DEBUG) + static_cast<int>(i));

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

may as well make getting the jsg::ContextData enum value a utility method given the repeats.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

done

@jasnell jasnell left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

couple of doc nits but otherwise LGTM

The current console decoration scheme breaks V8 snapshot creation in
two ways:

1) The C++ lambdas used as decorators hold v8::Global handles to the
   original console methods, but V8 does not support serializing Global
   handles.

2) The decorator for each console method is created through
   wrapSimpleFunction, which allocates an opaque JS wrapper using the
   Wrappable path and binds that wrapper to the resulting V8 function
   via V8::Function::New. The JS wrapper, which holds the C++ lambda,
   belongs to the context. However, V8 snapshot forbids pointers from
   isolate-level snapshot objects to context-level data. In this case,
   the function/template machinery belongs to the isolate-level snapshot,
   while the wrapper belongs to the context-level snapshot.

This patch addresses both issues:

- We preserve the original methods in a private, context-level
  embedder-data slots instead of holding them in v8::Global handles.
  This establishes edges from the context to those JS objects, so they
  remain reachable and are automatically captured in the snapshot as
  part of the context graph.

- We no longer use the Wrappable path for decoration. Instead, we create
  the V8 functions directly with V8::Function::New and use a plain C++
  functor that retrieves the required state from the context-level
  embedder-data slot. This avoids creating JS wrappers that hold
  references to C++ state.
@dbezhetskov

Copy link
Copy Markdown
Author

Thanks for the review, @jasnell !
The new version addresses all nits.

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.

3 participants