Skip to content

Conversation

@Prvnkmr337
Copy link
Contributor

@Prvnkmr337 Prvnkmr337 commented Jan 16, 2026

Fixes: AB#3492151

Problem:
The Broker's silent token request thread pool (sSilentExecutor) is fixed at 5 threads. Under high load scenarios with multiple concurrent ATS requests from various client apps (Teams, Outlook, OneAuth), this can cause request queuing, increased latency, and potential timeouts.

Solution:
Introduce a flight-controlled mechanism to expand the thread pool from 5 to 8 threads, applying ONLY to the Broker module (not MSAL client apps).

Changes:

  • CommonFlight.java: Add ENABLE_EXPANDED_BROKER_SILENT_THREAD_POOL flight (default: false)
  • CommandDispatcher.java: Add initializeSilentExecutorForBroker() and resetSilentRequestExecutorWithSize() methods with graceful executor shutdown handling
  • AndroidBrokerFlightsManager.kt: Call initializeBrokerSilentExecutor() during Broker initialization after flights are loaded

Key design decisions:

  • Uses Android process isolation: Broker runs in separate process, so static sSilentExecutor is independent from MSAL client apps
  • Broker-only initialization: AndroidBrokerFlightsManager is Broker-specific code that MSAL apps never call
  • Flight-controlled: Safe rollout via ECS with instant rollback capability
  • Graceful shutdown: Existing executor is properly terminated before creating expanded pool

…92151

Fixes: AB#3492151

Problem:
The Broker's silent token request thread pool (sSilentExecutor) is fixed at
5 threads. Under high load scenarios with multiple concurrent ATS requests
from various client apps (Teams, Outlook, OneAuth), this can cause request
queuing, increased latency, and potential timeouts.

Solution:
Introduce a flight-controlled mechanism to expand the thread pool from 5 to
8 threads, applying ONLY to the Broker module (not MSAL client apps).

Changes:
- CommonFlight.java: Add ENABLE_EXPANDED_BROKER_SILENT_THREAD_POOL flight
  (default: false)
- CommandDispatcher.java: Add initializeSilentExecutorForBroker() and
  resetSilentRequestExecutorWithSize() methods with graceful executor
  shutdown handling
- AndroidBrokerFlightsManager.kt: Call initializeBrokerSilentExecutor()
  during Broker initialization after flights are loaded

Key design decisions:
- Uses Android process isolation: Broker runs in separate process, so
  static sSilentExecutor is independent from MSAL client apps
- Broker-only initialization: AndroidBrokerFlightsManager is Broker-specific
  code that MSAL apps never call
- Flight-controlled: Safe rollout via ECS with instant rollback capability
- Graceful shutdown: Existing executor is properly terminated before
  creating expanded pool
@github-actions
Copy link

✅ Work item link check complete. Description contains link AB#3492151 to an Azure Boards work item.

@github-actions github-actions bot changed the title Add flight-controlled thread pool expansion for silent requests Add flight-controlled thread pool expansion for silent requests, Fixes AB#3492151 Jan 16, 2026
@Prvnkmr337 Prvnkmr337 marked this pull request as ready for review January 16, 2026 23:25
@Prvnkmr337 Prvnkmr337 requested review from a team as code owners January 16, 2026 23:25
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 introduces a flight-controlled mechanism to expand the Broker's silent token request thread pool from 5 to 8 threads. The change is Broker-specific and leverages Android process isolation to ensure MSAL client apps remain unaffected. The expansion is controlled by the ENABLE_EXPANDED_BROKER_SILENT_THREAD_POOL flight with a default value of false.

Changes:

  • Added new flight enum ENABLE_EXPANDED_BROKER_SILENT_THREAD_POOL for controlling thread pool expansion
  • Implemented initializeSilentExecutorForBroker() and resetSilentRequestExecutorWithSize() methods to dynamically resize the silent executor based on flight state
  • Updated changelog to reflect the thread pool size increase

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 10 comments.

File Description
common4j/src/main/com/microsoft/identity/common/java/flighting/CommonFlight.java Adds flight enum for enabling expanded broker silent thread pool with appropriate documentation
common4j/src/main/com/microsoft/identity/common/java/controllers/CommandDispatcher.java Implements broker-specific executor initialization with graceful shutdown and flight-based pool sizing logic
changelog.txt Documents thread pool size increase (though entry needs more context about flight-controlled nature)

prsaminathan added 2 commits January 20, 2026 10:41
1. Addressed copilot comments on telemetry, documentation and rest
   logic and tests.
@somalaya
Copy link
Contributor

Just curious, how did we arrive at the number for threads allowed for silent requests as 8? Is it because we did some stress testing and confirmed that it is performing better? Or are we going to try it out in PROD directly?

@Prvnkmr337
Copy link
Contributor Author

Just curious, how did we arrive at the number for threads allowed for silent requests as 8? Is it because we did some stress testing and confirmed that it is performing better? Or are we going to try it out in PROD directly?

The count is based on experiment being done on client side(WXP) thread pool size. For context the flow is:
WXP > OneAuth > IPC > Broker

So,

  1. WXP experimenting with 8 pool size
  2. OneAuth communicates with Broker via IPC
  3. Broker we want to match similar count, else 8 on WXP alone won't be beneficial

* <p>
* This method should ONLY be called by Broker during its initialization phase
* when the flight check determines that expanded pool is enabled.
* MSAL client apps should NOT call this method - they will use the default pool size.
Copy link
Contributor

Choose a reason for hiding this comment

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

Since you moved the flight check to broker in this iteration, if you want to avoid MSAL clients to avoid calling this method (in spite of the very clear Javadoc), you can use throwIfNotInvokedByBroker method.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

  1. Based on my code analysis, throwIfNotInvokedByBroker is part of BrokerHostingAppPublicApi
  2. My PR change initializeSilentExecutorWithExpandedPool() is in common module

Common module cannot referrer Broker API.

I understand the problem identified here that any non-Broker module ex: MSAL can call this API and change the thread pool size. I would like to take others thought, if we need more protection here or java doc is sufficient for now.

vis: @siddhijain

Copy link
Contributor

Choose a reason for hiding this comment

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

I think it is good to have that protection in place - Check the calling package and throw exception if anyone other than the Broker calls this method. If the javadoc is not read (which is generally the case), we won't even know if the MSAL clients start using thread pool size of 8.

}

if (!terminated) {
Logger.error(methodTag, "Executor did not fully terminate. Creating new executor anyway.", null);
Copy link
Contributor

Choose a reason for hiding this comment

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

Also what happens here if there are 2 Executors now? 1 with the thread pool size of 5 and another one with a size of 8?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There's only one static reference sSilentExecutor so we can't have two executors being actively used simultaneously. When we create a new executor at line 965, the old reference is replaced.

However, if we reach line 960-962 (executor didn't fully terminate), the old executor's threads may still be running in the background temporarily until their tasks complete or they're garbage collected. This is an edge case that:

  1. Should rarely occur - we wait 500ms for graceful shutdown, then call shutdownNow() which interrupts threads, then wait another 1000ms
  2. Has minimal impact - old tasks complete on old threads, new tasks use new executor
  3. Self-resolves - old executor is garbage collected once threads complete

This scenario is also no worse than before this PR, where resetSilentRequestExecutor() simply created a new executor without any shutdown, potentially leaving the old executor running indefinitely.

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.

4 participants