/** * Creates a bulkhead using a configuration supplied * * @param name the name of this bulkhead * @param bulkheadConfig custom bulkhead configuration */ public SemaphoreBulkhead(String name, BulkheadConfig bulkheadConfig) { this.name = name; this.config = bulkheadConfig != null ? bulkheadConfig : BulkheadConfig.ofDefaults(); // init semaphore this.semaphore = new Semaphore(this.config.getMaxConcurrentCalls(), true); this.metrics = new BulkheadMetrics(); this.eventProcessor = new BulkheadEventProcessor(); }
boolean tryEnterBulkhead() { boolean callPermitted = false; long timeout = config.getMaxWaitTime(); if (timeout == 0) { callPermitted = semaphore.tryAcquire(); } else { try { callPermitted = semaphore.tryAcquire(timeout, TimeUnit.MILLISECONDS); } catch (InterruptedException ex) { callPermitted = false; } } return callPermitted; }
@Test public void testBuildCustom() { // given int maxConcurrent = 66; long maxWait = 555; // when BulkheadConfig config = BulkheadConfig.custom() .maxConcurrentCalls(maxConcurrent) .maxWaitTime(maxWait) .build(); // then assertThat(config).isNotNull(); assertThat(config.getMaxConcurrentCalls()).isEqualTo(maxConcurrent); assertThat(config.getMaxWaitTime()).isEqualTo(maxWait); }
@Before public void setUp(){ helloWorldService = Mockito.mock(HelloWorldService.class); config = BulkheadConfig.custom() .maxConcurrentCalls(1) .build(); }
@Test public void changePermissionsCountWhileOneThreadIsWaitingForPermission() { BulkheadConfig originalConfig = BulkheadConfig.custom() .maxConcurrentCalls(1) .maxWaitTime(500000) .build(); SemaphoreBulkhead bulkhead = new SemaphoreBulkhead("test", originalConfig); assertThat(bulkhead.getMetrics().getAvailableConcurrentCalls()).isEqualTo(1); bulkhead.isCallPermitted(); assertThat(bulkhead.getMetrics().getAvailableConcurrentCalls()).isEqualTo(0); assertThat(bulkhead.getBulkheadConfig().getMaxConcurrentCalls()).isEqualTo(1); Thread bulkheadThread = new Thread(() -> { bulkhead.isCallPermitted(); bulkhead.onComplete(); }); bulkheadThread.setDaemon(true); bulkheadThread.start(); await().atMost(1, SECONDS) .until(() -> bulkheadThread.getState().equals(TIMED_WAITING)); assertThat(bulkhead.getMetrics().getAvailableConcurrentCalls()).isEqualTo(0); BulkheadConfig newConfig = BulkheadConfig.custom() .maxConcurrentCalls(2) .maxWaitTime(500000) .build(); bulkhead.changeConfig(newConfig); await().atMost(1, SECONDS) .until(() -> bulkheadThread.getState().equals(TERMINATED)); assertThat(bulkhead.getBulkheadConfig().getMaxConcurrentCalls()).isEqualTo(2); assertThat(bulkhead.getMetrics().getAvailableConcurrentCalls()).isEqualTo(1); }
/** * Creates a bulkhead with a default config. * * @param name the name of this bulkhead */ public SemaphoreBulkhead(String name) { this(name, BulkheadConfig.ofDefaults()); }
/** * {@inheritDoc} */ @Override public void changeConfig(final BulkheadConfig newConfig) { synchronized (configChangesLock) { int delta = newConfig.getMaxConcurrentCalls() - config.getMaxConcurrentCalls(); if (delta < 0) { semaphore.acquireUninterruptibly(-delta); } else if (delta > 0) { semaphore.release(delta); } config = newConfig; } }
@Test public void changePermissionsInIdleState() { BulkheadConfig originalConfig = BulkheadConfig.custom() .maxConcurrentCalls(3) .maxWaitTime(5000) .build(); SemaphoreBulkhead bulkhead = new SemaphoreBulkhead("test", originalConfig); assertThat(bulkhead.getBulkheadConfig().getMaxConcurrentCalls()).isEqualTo(3); assertThat(bulkhead.getBulkheadConfig().getMaxWaitTime()).isEqualTo(5000); BulkheadConfig newConfig = BulkheadConfig.custom() .maxConcurrentCalls(5) .maxWaitTime(5000) .build(); bulkhead.changeConfig(newConfig); assertThat(bulkhead.getBulkheadConfig().getMaxConcurrentCalls()).isEqualTo(5); assertThat(bulkhead.getBulkheadConfig().getMaxWaitTime()).isEqualTo(5000); newConfig = BulkheadConfig.custom() .maxConcurrentCalls(2) .maxWaitTime(5000) .build(); bulkhead.changeConfig(newConfig); assertThat(bulkhead.getBulkheadConfig().getMaxConcurrentCalls()).isEqualTo(2); assertThat(bulkhead.getBulkheadConfig().getMaxWaitTime()).isEqualTo(5000); bulkhead.changeConfig(newConfig); }
@Before public void setUp(){ helloWorldService = mock(HelloWorldService.class); config = BulkheadConfig.custom() .maxConcurrentCalls(1) .build(); bulkhead= Bulkhead.of("test", config); logger = mock(Logger.class); }
@Test public void changeWaitingTimeWhileOneThreadIsWaitingForPermission() { BulkheadConfig originalConfig = BulkheadConfig.custom() .maxConcurrentCalls(1) .maxWaitTime(500000) .build(); SemaphoreBulkhead bulkhead = new SemaphoreBulkhead("test", originalConfig); assertThat(bulkhead.getMetrics().getAvailableConcurrentCalls()).isEqualTo(1); bulkhead.isCallPermitted(); assertThat(bulkhead.getMetrics().getAvailableConcurrentCalls()).isEqualTo(0); assertThat(bulkhead.getBulkheadConfig().getMaxConcurrentCalls()).isEqualTo(1); Thread bulkheadThread = new Thread(() -> { bulkhead.isCallPermitted(); bulkhead.onComplete(); }); bulkheadThread.setDaemon(true); bulkheadThread.start(); await().atMost(1, SECONDS) .until(() -> bulkheadThread.getState().equals(TIMED_WAITING)); assertThat(bulkhead.getMetrics().getAvailableConcurrentCalls()).isEqualTo(0); BulkheadConfig newConfig = BulkheadConfig.custom() .maxConcurrentCalls(1) .maxWaitTime(0) .build(); bulkhead.changeConfig(newConfig); assertThat(bulkhead.tryEnterBulkhead()).isFalse(); // main thread is not blocked // previously blocked thread is still waiting await().atMost(1, SECONDS) .until(() -> bulkheadThread.getState().equals(TIMED_WAITING)); }
/** * Creates a BulkheadRegistry with a default Bulkhead configuration * * @return a BulkheadRegistry instance backed by a default Bulkhead configuration */ static BulkheadRegistry ofDefaults() { return new InMemoryBulkheadRegistry(BulkheadConfig.ofDefaults()); }
@Test public void shouldReturnTheCorrectName() { Bulkhead bulkhead = registry.bulkhead("test"); assertThat(bulkhead).isNotNull(); assertThat(bulkhead.getName()).isEqualTo("test"); assertThat(bulkhead.getBulkheadConfig().getMaxConcurrentCalls()).isEqualTo(25); assertThat(bulkhead.getMetrics().getAvailableConcurrentCalls()).isEqualTo(25); }
@Test public void changeWaitTimeInIdleState() { BulkheadConfig originalConfig = BulkheadConfig.custom() .maxConcurrentCalls(3) .maxWaitTime(5000) .build(); SemaphoreBulkhead bulkhead = new SemaphoreBulkhead("test", originalConfig); assertThat(bulkhead.getBulkheadConfig().getMaxConcurrentCalls()).isEqualTo(3); assertThat(bulkhead.getBulkheadConfig().getMaxWaitTime()).isEqualTo(5000); BulkheadConfig newConfig = BulkheadConfig.custom() .maxConcurrentCalls(3) .maxWaitTime(3000) .build(); bulkhead.changeConfig(newConfig); assertThat(bulkhead.getBulkheadConfig().getMaxConcurrentCalls()).isEqualTo(3); assertThat(bulkhead.getBulkheadConfig().getMaxWaitTime()).isEqualTo(3000); newConfig = BulkheadConfig.custom() .maxConcurrentCalls(3) .maxWaitTime(7000) .build(); bulkhead.changeConfig(newConfig); assertThat(bulkhead.getBulkheadConfig().getMaxConcurrentCalls()).isEqualTo(3); assertThat(bulkhead.getBulkheadConfig().getMaxWaitTime()).isEqualTo(7000); bulkhead.changeConfig(newConfig); }
@Test(expected = IllegalArgumentException.class) public void testBuildWithIllegalMaxConcurrent() { // when BulkheadConfig.custom() .maxConcurrentCalls(-1) .build(); }
@SuppressWarnings("Duplicates") @Test public void changePermissionsConcurrently() { BulkheadConfig originalConfig = BulkheadConfig.custom() .maxConcurrentCalls(3) .maxWaitTime(0) assertThat(bulkhead.getBulkheadConfig().getMaxConcurrentCalls()).isEqualTo(3); Thread bulkheadThread = new Thread(() -> { bulkhead.isCallPermitted(); bulkhead.changeConfig(BulkheadConfig.custom() .maxConcurrentCalls(1) .maxWaitTime(0) bulkhead.changeConfig(BulkheadConfig.custom() .maxConcurrentCalls(4) .maxWaitTime(0) .until(() -> secondChangerThread.getState().equals(TERMINATED)); assertThat(bulkhead.getBulkheadConfig().getMaxConcurrentCalls()).isEqualTo(4); assertThat(bulkhead.getMetrics().getAvailableConcurrentCalls()).isEqualTo(3); // main thread is still holding
@Test public void testConstructors() { final BulkheadRegistry registry = new InMemoryBulkheadRegistry(BulkheadConfig.ofDefaults()); BulkheadExports.ofIterable("boo_bulkheads", singleton(Bulkhead.ofDefaults("foo"))); BulkheadExports.ofBulkheadRegistry("boo_bulkheads", registry); BulkheadExports.ofSupplier("boo_bulkheads", () -> singleton(Bulkhead.ofDefaults("foo"))); BulkheadExports.ofIterable(singleton(Bulkhead.ofDefaults("foo"))); BulkheadExports.ofBulkheadRegistry(registry); BulkheadExports.ofSupplier(() -> singleton(Bulkhead.ofDefaults("foo"))); }
@SuppressWarnings("Duplicates") @Test public void changePermissionsCountWhileOneThreadIsRunningWithThisPermission() { BulkheadConfig originalConfig = BulkheadConfig.custom() .maxConcurrentCalls(1) .maxWaitTime(0) assertThat(bulkhead.getBulkheadConfig().getMaxConcurrentCalls()).isEqualTo(1); Thread bulkheadThread = new Thread(() -> { bulkhead.isCallPermitted(); BulkheadConfig newConfig = BulkheadConfig.custom() .maxConcurrentCalls(2) .maxWaitTime(0) assertThat(bulkhead.getBulkheadConfig().getMaxConcurrentCalls()).isEqualTo(2); assertThat(bulkhead.getBulkheadConfig().getMaxWaitTime()).isEqualTo(0); assertThat(bulkhead.getMetrics().getAvailableConcurrentCalls()).isEqualTo(1); assertThat(bulkhead.tryEnterBulkhead()).isTrue(); bulkhead.changeConfig(BulkheadConfig.custom() .maxConcurrentCalls(1) .maxWaitTime(0) assertThat(bulkhead.getBulkheadConfig().getMaxConcurrentCalls()).isEqualTo(2); assertThat(bulkhead.getBulkheadConfig().getMaxConcurrentCalls()).isEqualTo(1); assertThat(bulkhead.getBulkheadConfig().getMaxConcurrentCalls()).isEqualTo(1);
@Test(expected = IllegalArgumentException.class) public void testBuildWithIllegalMaxWait() { // when BulkheadConfig.custom() .maxWaitTime(-1) .build(); }
@Test public void testTryEnterWithTimeout() { // given BulkheadConfig config = BulkheadConfig.custom() .maxConcurrentCalls(1) .maxWaitTime(100) .build(); SemaphoreBulkhead bulkhead = new SemaphoreBulkhead("test", config); // when boolean entered = bulkhead.tryEnterBulkhead(); // then assertThat(entered).isTrue(); }
@Test public void testEntryTimeout() { // given BulkheadConfig config = BulkheadConfig.custom() .maxConcurrentCalls(1) .maxWaitTime(10) .build(); SemaphoreBulkhead bulkhead = new SemaphoreBulkhead("test", config); bulkhead.isCallPermitted(); // consume the permit // when boolean entered = bulkhead.tryEnterBulkhead(); // then assertThat(entered).isFalse(); }