Skip to content

Enable Virtual Threads by default, introduce JdkHttpApiHostClient, and refactor AppLogsWriter - #528

Open
ludoch wants to merge 1 commit into
mainfrom
virtualthreads-cl-975774097
Open

ludoch wants to merge 1 commit into
mainfrom
virtualthreads-cl-975774097

Conversation

@ludoch

@ludoch ludoch commented Oct 2, 2026

Copy link
Copy Markdown
Collaborator

This PR combines the changes from #523 and #526 along with internal code review and testing improvements:

Summary of Changes

  1. Virtual Threads Enabled by Default (EE10 / EE11):
    • Enables Jetty virtual threads by default (--enable_virtual_threads=true) when running on Java 21+, while still allowing opt-out via -Dappengine.use.virtualthreads=false or --enable_virtual_threads=false.
    • Removes the hard maxCarrierThreads = 4 cap on jdk.virtualThreadScheduler.maxPoolSize so the JVM defaults to availableProcessors(), while still respecting explicit user configuration (-Djdk.virtualThreadScheduler.maxPoolSize or JAVA_TOOL_OPTIONS).
  2. Virtual-Thread Friendly JdkHttpApiHostClient:
    • Introduces JdkHttpApiHostClient backed by java.net.http.HttpClient as the default APIHostClientInterface implementation (-Dappengine.api.http.client.use.jdk=true by default, with fallback to Jetty HttpApiHostClient via -Dappengine.api.http.client.use.jdk=false).
    • Properly enforces per-request deadlines via HttpRequest.Builder.timeout(...), handles response stream disconnection and cancellation on timeout/interrupt, and bounds default maxConcurrentRequests to 100.
  3. Avoid Virtual Thread Pinning in AppLogsWriter (runtime-shared):
    • Replaces synchronized blocks around ApiProxy.flushLogs() with a ReentrantLock (flushLock) and a thread-safe ConcurrentLinkedDeque + AtomicLong buffer byte counter so virtual threads do not pin carrier threads during blocking log flushes.
  4. Datastore beginTransaction() Retry Fix for Concurrency:
    • Adds a retry loop with exponential backoff and random jitter in BaseAsyncDatastoreServiceImpl.beginTransaction() when the local Datastore emulator returns an empty transaction handle under high virtual-thread concurrency.

…d refactor AppLogsWriter

Change-Id: I9757740970000000000000000000000000000002
| ApiProxy.RPCFailedException e) {
if (tx != null) {
try {
Future<Void> unused = tx.rollbackAsync();

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

useless

@ludoch
ludoch requested a review from lachlan-roberts October 2, 2026 20:46
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.

1 participant