Skip to content

perf: 4x faster startup — skip unnecessary driver reinstall, cache build products#3139

Open
qwertey6 wants to merge 2 commits intomobile-dev-inc:mainfrom
ReverentPeer:pr/2-performance
Open

perf: 4x faster startup — skip unnecessary driver reinstall, cache build products#3139
qwertey6 wants to merge 2 commits intomobile-dev-inc:mainfrom
ReverentPeer:pr/2-performance

Conversation

@qwertey6
Copy link
Copy Markdown

@qwertey6 qwertey6 commented Apr 4, 2026

Proposed changes

~4x faster startup time. Every maestro test invocation was spending ~40-45 seconds killing and reinstalling the XCTest driver, even when the exact same driver was already running and healthy. This PR makes driver reuse the default.

Impact

Scenario Before After Improvement
Single device, warm ~52s ~10s 5.2x faster
Single device, cold ~52s ~12s 4.3x faster
3 devices parallel ~54s ~15s 3.6x faster

Root cause of the slowness

  1. reinstallDriver=true was the default: Every run killed the XCTest runner, extracted build products from the JAR, ran xcodebuild test-without-building, and polled for ~40s until the runner responded.
  2. The isChannelAlive() optimization was dead code: startXCTestRunner() checks if the runner is already alive, but restartXCTestRunner() called uninstall() first — killing the runner before the alive check could ever succeed.
  3. Build products extracted to temp dir every run: ~2-3s spent unzipping the same unchanged binaries from the JAR on every invocation.
  4. 100-second HTTP read timeout on status check: The polling loop used a 100s read timeout for a health check that should respond instantly. If the server accepted a connection but hung, one poll iteration blocked for 100s.

Changes

  • Default reinstallDriver to false across all entry points (TestCommand, PrintHierarchyCommand, MaestroSessionManager, AndroidDriver, LocalXCTestInstaller, XCTestDriverClient)
  • Check isChannelAlive() before reinstalling: restartXCTestRunner() now checks if the runner is responding before deciding to start fresh. Explicit --reinstall-driver still forces a full reinstall.
  • Cache build products per-device: Extracted to ~/.maestro/build-products/<deviceId>/ with SHA-256 hash of JAR source. Cache is validated on each run — skips extraction when hash matches, re-extracts automatically on Maestro upgrade or local rebuild.
  • Reduce status check timeout: 100s → 3s. The /status endpoint should respond instantly if healthy.

Verification

Scenario Result Time
Cold start, no driver, no cache Pass 19s
Warm, driver still running Pass 10s
Explicit --reinstall-driver Pass 25s
Driver killed externally, recover Pass 10s
3 devices parallel, cold Pass 15s
3 devices parallel, warm Pass 11s

Testing

Manually verified on iOS simulators and Android emulator. All existing tests pass.

Depends on: #3138 (parallel iOS execution)
Next: PR 3 adds version safety (hash checks to prevent stale drivers after Maestro upgrade)

Issues fixed

Closes #1096

qwertey6 and others added 2 commits April 4, 2026 15:37
iOS simulators share the host's localhost, causing port collisions when
multiple Maestro processes target different sims simultaneously. Session
tracking was per-platform, so two processes on different devices would
interfere with each other's sessions.

Changes:
- Per-device session tracking: SessionStore keys are now
  "{platform}_{deviceId}_{sessionId}" instead of "{platform}_{sessionId}"
- Add --driver-host-port CLI flag for explicit XCTest server port
- Auto-select available ports with isPortAvailable() check
- Refactor SessionStore from singleton to injectable class (DI)
- Add shouldCloseSession(platform, deviceId) for per-device shutdown
  instead of global activeSessions().isEmpty()
- Add cross-process file locking to KeyValueStore (~/.maestro/sessions)
- Append PID to debug log directory to prevent parallel race
- Enable useJUnitPlatform() in maestro-cli (was missing)
- Add SessionStoreTest with 8 tests covering isolation and lifecycle

Verified: 3 iOS simulators + Android emulator running simultaneously,
all passing. Both --driver-host-port (explicit) and auto-port-selection
work correctly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Default --reinstall-driver to false: reuse a healthy running driver
  instead of killing and reinstalling on every run (~40s saved on iOS)
- XCTestDriverClient checks isChannelAlive() before reinstalling —
  if the user explicitly passes --reinstall-driver, honor it
- Cache extracted iOS build products per-device in
  ~/.maestro/build-products/<deviceId>/ with SHA-256 hash validation:
  skips extraction when source matches cache, re-extracts on upgrade
- Reduce XCTest status check HTTP read timeout from 100s to 3s
- Remove Thread.sleep(1000) heartbeat delay hack (no longer needed
  with per-device session tracking)

Single device: ~52s → ~10-12s. Three devices parallel: ~54s → ~18s.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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.

Make Maestro faster by giving the option to skip helper APKs install/uninstall

1 participant