Repository navigation
fix(scripting/v8): use node's setImmediate on the server - #4402
Open
OneEarJoe5 wants to merge 1 commit into
Open
OneEarJoe5 wants to merge 1 commit into
OneEarJoe5 wants to merge 1 commit into
Conversation
timer.js replaces setImmediate with a tick-based timer, so on the server every setImmediate waits for the next server frame (~50 ms). The node runtime already polls the resource's uv loop every millisecond, so use node's own setImmediate there. Callbacks still run through runWithBoundaryStart, errors still go through printError and they get their own profiler scope since they now run outside the resource tick. The handle keeps the id/refresh/number shape of our timer handles and clearImmediate and clearTimeout clear both kinds. setTimeout, setInterval, setTick and the client are unchanged. Signed-off-by: OneEarJoe5 <212275319+OneEarJoe5@users.noreply.github.com>
This was referenced Nov 14, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Goal of this PR
Server-side JS that awaits DB queries (or anything else that yields with
setImmediate) waits a full server frame per step.timer.jsoverridessetImmediatewithsetTimeout(cb, 0), and those timers only run in the resource tick, so ~50 ms each. The I/O itself is fast, the node runtime polls each resource's uv loop every 1 ms (UvLoopTimer), only the timer is slow. That's the 50 ms in #4186: postgres-js only writes a query in asetImmediate(src/connection.js, write batching), so every query pays a frame.How is this PR achieving the goal
When
timer.jsruns inside the node runtime (process.versions.nodeis set) it keeps node's ownsetImmediateinstead of the tick-based fallback.runWithBoundaryStart, errors still go throughprintError('timer', ...)and the server keeps going.setImmediate (<resource>). Otherwise their time would just disappear fromprofilerrecordings.id,refresh(), converts to a number), so code that stores it as a number or key keeps working.clearImmediateandclearTimeoutwork on both kinds.setTimeout,setInterval,setTickand the client side (noprocessthere) are untouched, so onlysetImmediategets faster.I kept it to
setImmediateon purpose.setTimeouttiming is tick based on both sides and scripts may rely on that, so that felt like a bigger change than needed for this.This PR applies to the following area(s)
Server, ScRT: JS
Successfully tested on
Platforms: Windows (server), Linux (server, WSL2 Ubuntu 24.04)
Windows: server built from current master (5412ed8). Linux: the official 38621 artifact, which is built from that same commit, with only
timer.jsswapped. Same test resources on both, before and after this change.The case from the issue with the real postgres-js 3.4.5 against PostgreSQL 16 (same loop as in #4186):
SELECT 1)Everything else, Windows (Linux gave the same results for every row it ran, see below):
await new Promise(r => setImmediate(r))setImmediatelikeconnection.jsdoes, reply over local TCP)setImmediateclearImmediate/clearTimeouton an immediate, clearing null/unknown/already firedemitinside the callbackid,refresh()hasRef()afterunref()truefalse, thentrueagain afterref()(node's real value, the uv loop is polled anyway so it makes no other difference)refresh()on a pending immediateprofiler recordwith 5 ms of work per immediatetick (<resource>)(177 ms)setImmediate (<resource>)(166–185 ms), scopes stay balanced, also when the callback throwssetTimeout(0),setInterval,setTickClient built from the same master, same test as a client script with this exact commit: there's no
processon the client, sosetImmediatestill takes one frame like before, and clearing, args and errors behave the same.On Linux I ran the same resource for everything except txAdmin and oxmysql: timing, order/args, clearing, errors, async rejections, natives, handle shape,
hasRef/refresh,setTimeout/setInterval/setTick, 10000 immediates, restart from outside, the profiler (tick (jstiming)181 ms before,setImmediate (jstiming)157 ms after) and the heap test (~5 MB flat both ways). Same results as on Windows.Something I noticed that this PR does not change: awaiting a callback from another JS resource (e.g.
exports.oxmysql.query(..., cb)from a JS resource) still takes ~50 ms, with or without this change. Looks like microtasks aren't drained after a ref call into the node runtime, so the continuation waits for the next tick. That's a separate C++ change, happy to look at it in its own PR if wanted.One thing I ran into while testing, unrelated to this PR: a JS resource that restarts itself (
ExecuteCommand('restart <self>')) from inside a timer callback crashes the server. Happens on master withsetTimeoutand withsetImmediate, with or without this change. I'll open a separate issue for that.Checklist
Fixes issues
fixes #4186