Skip to content

perf: memoize the browser's CDP connection - #61

Open
hugo-fasone wants to merge 1 commit into
PrestaFlow:mainfrom
hugo-fasone:memoisation-connexion
Open

perf: memoize the browser's CDP connection#61
hugo-fasone wants to merge 1 commit into
PrestaFlow:mainfrom
hugo-fasone:memoisation-connexion

Conversation

@hugo-fasone

Copy link
Copy Markdown

Context

On any suite that calls importPage(), runtime is dominated by redundant CDP
reconnections rather than by actual browser work. Measured on a project with 2 suites /
5 tests (PHP 8.3, headless Chromium in a container, PS 9.1 target): 23.67 s, of
which roughly 15 s was pure bootstrap.

Launching Chromium accounts for only 208 ms — the cost is elsewhere.

Root cause

TestsSuite::getBrowser() re-reads the socket file and issues a fresh
BrowserFactory::connectToBrowser() on every call, never reusing the instance. Two
effects compound:

OperationMeasured cost
connectToBrowser()101 ms, constant
getPages()205 → 506 → 406 → 660 ms (growing: every connection left open makes target enumeration heavier)
TestsSuite::getBrowser()~102 ms
TestsSuite::getPage()~1,250 ms (2 × getBrowser())

The multiplier lives in CommonPage::__call(), which evaluates getPage()three
times
— condition, method_exists, invocation:

if (!is_null($this->getPage()) && method_exists($this->getPage(), $name)) {
call_user_func_array([$this->getPage(), $name], $arguments);
}

And importPage() calls $pageInstance->setUserAgent(...), which does not exist on the
library's Page classes and therefore goes through that __call. Full chain:

importPage() → setUserAgent() → __call → 3 × getPage() → 6 × connectToBrowser
≈ 3 × 1,250 ms = 3.7 s per imported page

A suite importing 3 pages thus paid ~11 s before running its first test.

Changes

1. TestsSuite::getBrowser() — reuse the connected instance

Two statics ($browserInstance, $browserInstanceSocket) plus a guard at the top of the
method. Invalidation has three conditions, so a dead browser is never handed back:

  • the socket URI is gone (end of run, @unlink by ExecuteSuite);
  • the URI differs from the memoized one (browser relaunched elsewhere);
  • getConnection()->isConnected() is false (browser closed or crashed).

isConnected() performs no CDP round-trip — it only reads the local socket state in
wrench\Client — so the guard stays free. ExecuteSuite::getBrowser(force: false)?->close()
still retrieves the live instance to close it at the end of the run.

2. TestsSuite::getPage() — one enumeration per attempt

getBrowser() is hoisted into a variable, and getPages() is only re-issued after an
actual createPage() (the first list is otherwise already current). The ×3 retry loop and
the applyExtraHttpHeaders() semantics are unchanged.

3. CommonPage::__call() — evaluate getPage() once

Results

BeforeAfter
getBrowser() (already connected)102 ms, constant0 ms
getPage()~1,250 ms166 ms, then 0 ms
init() = 1 × importPage()3,910 ms3 ms
Smoke suite (3 tests)10.53 s3.37 s
Full run (2 suites, 5 tests)23.67 s3.60 s

Compatibility

No API change and no observable behaviour change:

  • __call() still returns nothing — I deliberately did not add a return on
    call_user_func_array, since that would change the return value of every proxied call
    and is out of scope for a performance fix;
  • getPage() keeps its return $pages[0] (same semantics on an empty list) and its retry
    loop;
  • a dead browser now raises an error instead of being silently relaunched mid-suite. This
    is the more honest behaviour: the previous silent relaunch dropped the session and
    cookies, producing failures that were hard to interpret.

Testing

  • vendor/bin/phpunit --testsuite Unit: OK (117 tests, 442 assertions) on PHP 8.3.
  • Integration run against a real PS 9.1 shop: 5 tests, same results as before the patch.
  • The full importPage() path (3 pages, with BO credentials) exercised separately: imports
    succeed and the resulting failure is the login assertion, as expected.

Sign up for freeto 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.

1 participant

@hugo-fasone