Skip to content

Conversation

@habdelra
Copy link
Contributor

This PR updates how our prerenderer works so that all chrome tabs are used for rendering, such that a single server can concurrently handle multiple render requests.

  • Update AWS infra to scale down the number of prerender servers by 25%
  • Add PRERENDER_REALM_TAB_MAX env var to parameter store and infra

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 234084aa5b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +392 to +395
let entries = this.#realmPages.get(realm);
let entryList = entries
? [...entries].filter((entry) => !entry.closing && !entry.transitioning)
: [];

Choose a reason for hiding this comment

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

P1 Badge Allow same-realm queueing during tab transitions

In #selectEntryForRealm, the entry list for a realm excludes any tab marked transitioning (filter((entry) => !entry.closing && !entry.transitioning)). When a cross‑realm request queues on a busy tab, the method sets transitioning = true for that tab later in this function, so a subsequent request for the original realm no longer sees its own tab to queue behind. If the pool is full (e.g., maxPages = 1 and no standby), the same‑realm request now throws “No standby page available” even though the tab would become free shortly. This only happens under concurrent cross‑realm load, but it turns what could be a queued render into a hard failure. Consider letting same‑realm requests queue on transitioning tabs (or scoping transitioning to cross‑realm selection) to avoid these false “no capacity” errors.

Useful? React with 👍 / 👎.

@habdelra habdelra marked this pull request as draft January 16, 2026 21:56
@github-actions
Copy link

Host Test Results

    1 files  ±  0      1 suites  ±0   1h 35m 59s ⏱️ + 7m 2s
1 887 tests +218  1 872 ✅ +220  15 💤 ±0  0 ❌  - 1 
1 902 runs  +219  1 887 ✅ +222  15 💤 ±0  0 ❌  - 2 

Results for commit 234084a. ± Comparison against base commit 90d8c13.

This pull request removes 1 and adds 219 tests. Note that renamed tests count towards both.
Chrome ‑ Global error: Uncaught TypeError: Failed to fetch at http://localhost:7357/assets/chunk.b9c2f4bf6c1283ea8e7a.js, line 150984  While executing test: Integration | card-copy: copy button does not appear when there is 1 stack for single card item 
Chrome ‑ Integration | Store: added instance that was previously not saved will begin to auto save after being added
Chrome ‑ Integration | Store: an instance can be restored after a loader reset
Chrome ‑ Integration | Store: an instance can debounce auto saves
Chrome ‑ Integration | Store: an instance can live update thru an error state
Chrome ‑ Integration | Store: an instance live updates from indexing events for a code update
Chrome ‑ Integration | Store: an instance live updates from indexing events for an instance update
Chrome ‑ Integration | Store: an instance that started out with a local ID can be restored after a loader reset
Chrome ‑ Integration | Store: an instance will NOT auto save when its data changes, if the user does not have write permissions
Chrome ‑ Integration | Store: an instance will auto save when its data changes
Chrome ‑ Integration | Store: an unsaved instance live updates when realm event matching local ID is received
…

@habdelra habdelra requested a review from Copilot January 16, 2026 22:24
@habdelra habdelra marked this pull request as ready for review January 16, 2026 22:24
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR refactors the prerenderer to enable concurrent rendering across multiple Chrome tabs per server, replacing the previous per-realm serialization model. The changes introduce tab-level queuing and a configurable per-realm tab maximum to optimize resource utilization.

Changes:

  • Removed global per-realm serialization, moving concurrency control to tab-level queuing within PagePool
  • Introduced PRERENDER_REALM_TAB_MAX environment variable to limit concurrent tabs per realm
  • Updated RenderRunner to use a lease/release pattern with try-finally blocks for proper resource cleanup

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.

File Description
packages/realm-server/tests/prerendering-test.ts Added TestSemaphore and comprehensive tests for concurrent tab usage, queuing behavior, and tab reassignment logic
packages/realm-server/prerender/render-runner.ts Refactored to acquire page leases with release callbacks and ensure cleanup via try-finally blocks; simplified auth change detection
packages/realm-server/prerender/prerenderer.ts Removed per-realm promise chaining and global semaphore acquisition; simplified retry logic by delegating concurrency control to PagePool
packages/realm-server/prerender/page-pool.ts Implemented TabQueue for tab-level queuing, added tab selection logic favoring idle aligned tabs, and introduced transitioning/closing flags to prevent race conditions

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

'tab realigned to realm-b when queued request starts',
);
third.release();
blocker.release();
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

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

The blocker lease is released twice (lines 2263 and 2276). This could cause issues if the release function is not idempotent. While the PagePool's release function appears to check for double-release, this is confusing and may indicate a logic error in the test.

Suggested change
blocker.release();

Copilot uses AI. Check for mistakes.
Comment on lines +351 to +355
shortCircuit = true;
break;
}
if (this.#isAuthError(error)) {
shortCircuit = true;
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

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

The value assigned to shortCircuit here is unused.

Suggested change
shortCircuit = true;
break;
}
if (this.#isAuthError(error)) {
shortCircuit = true;
break;
}
if (this.#isAuthError(error)) {

Copilot uses AI. Check for mistakes.
break;
}
if (this.#isAuthError(error)) {
shortCircuit = true;
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

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

The value assigned to shortCircuit here is unused.

Suggested change
shortCircuit = true;

Copilot uses AI. Check for mistakes.
isolatedHTML = capture.value;
} else {
let capErr = this.#captureToError(capture);
if (!error && capErr) {
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

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

This negation always evaluates to true.

Suggested change
if (!error && capErr) {
if (capErr) {

Copilot uses AI. Check for mistakes.
shortCircuit = true;
break;
for (let step of steps) {
if (shortCircuit) break;
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

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

This use of variable 'shortCircuit' always evaluates to false.

Suggested change
if (shortCircuit) break;

Copilot uses AI. Check for mistakes.
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 234084aa5b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +186 to +188
} else {
for (let entry of entries) {
void this.#closeEntry(entry);

Choose a reason for hiding this comment

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

P2 Badge Prevent oversubscribing contexts during async eviction

In the awaitIdle: false branch, the realm is removed from #realmPages and each entry is closed asynchronously. Because pool sizing (#poolEntryCount/#totalContextCount) only counts entries still in #realmPages, the soon‑to‑be‑closed contexts are no longer counted and #ensureStandbyPool() can spin up new standbys while the old contexts are still running. Under timeout/unusable evictions (the new awaitIdle: false path), this can temporarily exceed the configured maxPages+1 and spike Chrome context count/memory. Consider keeping closing entries in the map (marked closing) or deferring standby creation until closes finish to preserve the capacity limit.

Useful? React with 👍 / 👎.

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