Skip to content

Commit c65c290

Browse files
committed
Now using bound executor pool with a limit of 100.
1 parent 4a1c1e9 commit c65c290

2 files changed

Lines changed: 61 additions & 3 deletions

File tree

google-auth-library-java/oauth2_http/java/com/google/auth/oauth2/RegionalAccessBoundaryManager.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,16 +88,22 @@ final class RegionalAccessBoundaryManager {
8888
// on concurrent refresh tasks, while threadCount provides unique names
8989
// for easier debugging.
9090
private static final AtomicInteger threadCount = new AtomicInteger(0);
91+
92+
// Bounded executor service ensures hard limits on concurrent refresh tasks and queued tasks
93+
// to avoid resource exhaustion.
94+
private static final int EXECUTOR_POOL_SIZE = 5;
95+
private static final int EXECUTOR_QUEUE_CAPACITY = 100;
96+
9197
private static final ExecutorService EXECUTOR;
9298

9399
static {
94100
ThreadPoolExecutor executor =
95101
new ThreadPoolExecutor(
96-
5, // corePoolSize: threads to keep alive
97-
5, // maximumPoolSize: max threads allowed
102+
EXECUTOR_POOL_SIZE, // corePoolSize: threads to keep alive
103+
EXECUTOR_POOL_SIZE, // maximumPoolSize: max threads allowed
98104
1, // keepAliveTime: time to wait before terminating idle threads
99105
TimeUnit.HOURS, // unit for keepAliveTime
100-
new LinkedBlockingQueue<>(), // work queue
106+
new LinkedBlockingQueue<>(EXECUTOR_QUEUE_CAPACITY), // work queue with bound
101107
r -> {
102108
Thread t = new Thread(r, "RAB-refresh-" + threadCount.getAndIncrement());
103109
t.setDaemon(true);

google-auth-library-java/oauth2_http/javatests/com/google/auth/oauth2/RegionalAccessBoundaryTest.java

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
import java.io.ObjectInputStream;
4646
import java.io.ObjectOutputStream;
4747
import java.util.Collections;
48+
import java.util.concurrent.CountDownLatch;
4849
import java.util.concurrent.atomic.AtomicLong;
4950
import org.junit.jupiter.api.AfterEach;
5051
import org.junit.jupiter.api.BeforeEach;
@@ -224,6 +225,57 @@ public void testManagerTriggersRefreshInGracePeriod() throws InterruptedExceptio
224225
assertEquals(newerEncoded, resultRab.getEncodedLocations());
225226
}
226227

228+
@Test
229+
public void testExecutorQueueCapacityLimit() throws Exception {
230+
final String url = "https://example.com/rab";
231+
final AccessToken token = new AccessToken("token", new java.util.Date(System.currentTimeMillis() + 3600000L));
232+
RegionalAccessBoundaryProvider provider = () -> url;
233+
234+
int poolSize = 5;
235+
int queueCapacity = 100;
236+
int totalCapacity = poolSize + queueCapacity;
237+
238+
CountDownLatch latch = new CountDownLatch(1);
239+
240+
java.io.InputStream blockingStream = new java.io.InputStream() {
241+
private final java.io.InputStream delegate = new ByteArrayInputStream("{\"encodedLocations\": \"encoded\", \"locations\": [\"loc\"]}".getBytes());
242+
private boolean blocked = false;
243+
244+
@Override
245+
public int read() throws java.io.IOException {
246+
if (!blocked) {
247+
try {
248+
latch.await();
249+
} catch (InterruptedException e) {
250+
Thread.currentThread().interrupt();
251+
}
252+
blocked = true;
253+
}
254+
return delegate.read();
255+
}
256+
};
257+
258+
MockHttpTransport transport = new MockHttpTransport.Builder()
259+
.setLowLevelHttpResponse(new MockLowLevelHttpResponse().setContent(blockingStream).setContentType("application/json"))
260+
.build();
261+
HttpTransportFactory transportFactory = () -> transport;
262+
263+
RegionalAccessBoundaryManager[] managers = new RegionalAccessBoundaryManager[totalCapacity];
264+
for (int i = 0; i < totalCapacity; i++) {
265+
managers[i] = new RegionalAccessBoundaryManager(testClock);
266+
managers[i].triggerAsyncRefresh(transportFactory, provider, token);
267+
}
268+
269+
RegionalAccessBoundaryManager extraManager = new RegionalAccessBoundaryManager(testClock);
270+
assertFalse(extraManager.isCooldownActive());
271+
272+
extraManager.triggerAsyncRefresh(transportFactory, provider, token);
273+
274+
assertTrue(extraManager.isCooldownActive(), "106th task should have been rejected and entered cooldown");
275+
276+
latch.countDown();
277+
}
278+
227279
private static class TestClock implements Clock {
228280
private final AtomicLong currentTime = new AtomicLong(System.currentTimeMillis());
229281

0 commit comments

Comments
 (0)