-
Notifications
You must be signed in to change notification settings - Fork 29
fix: Improve Circuit breaker behaviour #1017
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 11 commits
b43e388
87aab24
0ec0870
7b0e572
077b784
75e300b
5475777
2501904
31924b3
4ab435e
a3b144f
689acb8
624c10a
3e05498
d573e51
6cf6d0f
d8f9139
d62b970
5ba5226
0bf62f6
7b6e46e
1bc4f01
2aef26b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -26,6 +26,7 @@ | |
| import lombok.Getter; | ||
| import lombok.RequiredArgsConstructor; | ||
| import lombok.extern.slf4j.Slf4j; | ||
| import lombok.val; | ||
|
|
||
| @Slf4j | ||
| @RequiredArgsConstructor( access = AccessLevel.PRIVATE ) | ||
|
|
@@ -52,6 +53,8 @@ class GetOrComputeSingleDestinationCommand | |
| private final DestinationServiceTokenExchangeStrategy exchangeStrategy; | ||
| @Nullable | ||
| private final GetOrComputeAllDestinationsCommand getAllCommand; | ||
| @Nonnull | ||
| private final Boolean prependGetAllDestinationCall; | ||
|
|
||
| @SuppressWarnings( "deprecation" ) | ||
| static Try<GetOrComputeSingleDestinationCommand> prepareCommand( | ||
|
|
@@ -60,7 +63,8 @@ static Try<GetOrComputeSingleDestinationCommand> prepareCommand( | |
| @Nonnull final Cache<CacheKey, Destination> destinationCache, | ||
| @Nonnull final Cache<CacheKey, ReentrantLock> isolationLocks, | ||
| @Nonnull final BiFunction<String, DestinationOptions, Destination> destinationRetriever, | ||
| @Nullable final GetOrComputeAllDestinationsCommand getAllCommand ) | ||
| @Nullable final GetOrComputeAllDestinationsCommand getAllCommand, | ||
| @Nonnull final Boolean prependGetAllDestinationCall ) | ||
| { | ||
| final Supplier<Destination> destinationSupplier = | ||
| () -> destinationRetriever.apply(destinationName, destinationOptions); | ||
|
|
@@ -109,7 +113,8 @@ static Try<GetOrComputeSingleDestinationCommand> prepareCommand( | |
| destinationCache, | ||
| destinationSupplier, | ||
| exchangeStrategy, | ||
| getAllCommand)); | ||
| getAllCommand, | ||
| prependGetAllDestinationCall)); | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -149,6 +154,10 @@ Try<Destination> execute() | |
| if( result != null ) { | ||
| return Try.success(result); | ||
| } | ||
| if( prependGetAllDestinationCall && !destinationExists() ) { | ||
| final String msg = "Destination %s was not found among the destinations of the current tenant."; | ||
| return Try.failure(new DestinationAccessException(String.format(msg, destinationName))); | ||
| } | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (Question/Minor) Not a huge fan of
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I struggled a bit with placing this into |
||
|
|
||
| final Try<Destination> maybeResult = Try.ofSupplier(destinationSupplier); | ||
| if( maybeResult.isFailure() ) { | ||
|
|
@@ -263,6 +272,20 @@ private Destination getCachedDestination() | |
| return maybeDestination; | ||
| } | ||
|
|
||
| private boolean destinationExists() | ||
| { | ||
| if( getAllCommand != null ) { | ||
| val allDestinations = getAllCommand.execute(); | ||
| if( allDestinations != null && allDestinations.isSuccess() ) { | ||
| return allDestinations | ||
| .get() | ||
| .stream() | ||
| .anyMatch(properties -> properties.get(DestinationProperty.NAME).contains(destinationName)); | ||
| } | ||
| } | ||
| return false; | ||
| } | ||
|
|
||
| /** | ||
| * currently this effectively limits the cache duration to the change detection interval, because change detection | ||
| * on certificates isn't possible in all cases See the note on {@link DestinationServiceFactory} where this property | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -113,6 +113,15 @@ class DestinationServiceTest | |
| "ProxyType": "Internet", | ||
| "KeyStorePassword": "password", | ||
| "KeyStoreLocation": "aaa" | ||
| }, | ||
| { | ||
| "Name": "SomeDestinationName", | ||
| "Type": "HTTP", | ||
| "URL": "https://a.s4hana.ondemand.com", | ||
| "Authentication": "ClientCertificateAuthentication", | ||
| "ProxyType": "Internet", | ||
| "KeyStorePassword": "password", | ||
| "KeyStoreLocation": "aaa" | ||
| }] | ||
| """; | ||
|
|
||
|
|
@@ -442,7 +451,7 @@ void testGettingDestinationProperties() | |
|
|
||
| assertThat(destinationList) | ||
| .extracting(d -> d.get(DestinationProperty.NAME).get()) | ||
| .containsExactly("CC8-HTTP-BASIC", "CC8-HTTP-CERT1", "CC8-HTTP-CERT"); | ||
| .containsExactly("CC8-HTTP-BASIC", "CC8-HTTP-CERT1", "CC8-HTTP-CERT", destinationName); | ||
|
|
||
| final DestinationProperties destination = | ||
| destinationList | ||
|
|
@@ -478,7 +487,7 @@ void testGettingDestinationPropertiesProvider() | |
| final Collection<DestinationProperties> destinationList = loader.getAllDestinationProperties(ALWAYS_PROVIDER); | ||
| assertThat(destinationList) | ||
| .extracting(d -> d.get(DestinationProperty.NAME).get()) | ||
| .containsExactly("CC8-HTTP-BASIC", "CC8-HTTP-CERT1", "CC8-HTTP-CERT"); | ||
| .containsExactly("CC8-HTTP-BASIC", "CC8-HTTP-CERT1", "CC8-HTTP-CERT", destinationName); | ||
| } | ||
|
|
||
| @Test | ||
|
|
@@ -494,7 +503,7 @@ void testGettingDestinationPropertiesSubscriber() | |
| final Collection<DestinationProperties> destinationList = loader.getAllDestinationProperties(ONLY_SUBSCRIBER); | ||
| assertThat(destinationList) | ||
| .extracting(d -> d.get(DestinationProperty.NAME).get()) | ||
| .containsExactly("CC8-HTTP-BASIC", "CC8-HTTP-CERT1", "CC8-HTTP-CERT"); | ||
| .containsExactly("CC8-HTTP-BASIC", "CC8-HTTP-CERT1", "CC8-HTTP-CERT", destinationName); | ||
| } | ||
|
|
||
| @Test | ||
|
|
@@ -589,10 +598,10 @@ void testGetAllDestinationsForProvider() | |
| final List<Destination> destinationList = new ArrayList<>(); | ||
| destinations.get().forEach(destinationList::add); | ||
|
|
||
| assertThat(destinationList.size()).isEqualTo(3); | ||
| assertThat(destinationList.size()).isEqualTo(4); | ||
| assertThat(destinationList) | ||
| .extracting(d -> d.get(DestinationProperty.NAME).get()) | ||
| .containsOnly("CC8-HTTP-BASIC", "CC8-HTTP-CERT", "CC8-HTTP-CERT1"); | ||
| .containsOnly("CC8-HTTP-BASIC", "CC8-HTTP-CERT", "CC8-HTTP-CERT1", destinationName); | ||
| } | ||
|
|
||
| @SuppressWarnings( "deprecation" ) | ||
|
|
@@ -1910,4 +1919,56 @@ private String createHttpDestinationServiceResponse( final String name, final St | |
| } | ||
| """, name, url); | ||
| } | ||
|
|
||
| @Test | ||
| void testPrependGetAllDestinationsCall() | ||
| { | ||
| doReturn(responseServiceInstanceDestination) | ||
| .when(destinationServiceAdapter) | ||
| .getConfigurationAsJson(eq("/v1/instanceDestinations"), any()); | ||
| doReturn(responseSubaccountDestination) | ||
| .when(destinationServiceAdapter) | ||
| .getConfigurationAsJson(eq("/v1/subaccountDestinations"), any()); | ||
|
|
||
| DestinationService.Cache.setGetAllDocumentsPrepended(true); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (Major) Please consider different API options, to reflect on existing available methods. - DestinationService.Cache.setGetAllDocumentsPrepended(true);
+ DestinationService.Cache.enableGetAllDocumentsPrepended();
+ DestinationService.Cache.enableGetAllDestinationsPrepended();
+ DestinationService.Cache.enablePreLookupCheck();
+ DestinationService.Cache.enableExistenceCheck();Also your feature would be enabled by default - right?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Renamed it to Right now I have so that the behaviour is disabled by default. I did that to not introduce breaking changes (and also we talked about that this might increase response times for the first call). Do you think it should be enabled by default? |
||
|
|
||
| final DestinationOptions options = | ||
| DestinationOptions.builder().augmentBuilder(augmenter().retrievalStrategy(ALWAYS_PROVIDER)).build(); | ||
| Destination result = loader.tryGetDestination(destinationName).get(); | ||
|
|
||
| // verify all results are cached | ||
| verify(destinationServiceAdapter, times(1)).getConfigurationAsJson(eq("/v1/instanceDestinations"), any()); | ||
| verify(destinationServiceAdapter, times(1)).getConfigurationAsJson(eq("/v1/subaccountDestinations"), any()); | ||
| verify(destinationServiceAdapter, times(1)) | ||
| .getConfigurationAsJson(eq("/v1/destinations/" + destinationName), any()); | ||
| verifyNoMoreInteractions(destinationServiceAdapter); | ||
|
|
||
| DestinationService.Cache.setGetAllDocumentsPrepended(false); | ||
| } | ||
|
|
||
| @Test | ||
| void testPrependGetAllDestinationsCallWithMissingDestination() | ||
| { | ||
| doReturn(responseServiceInstanceDestination) | ||
| .when(destinationServiceAdapter) | ||
| .getConfigurationAsJson(eq("/v1/instanceDestinations"), any()); | ||
| doReturn(responseSubaccountDestination) | ||
| .when(destinationServiceAdapter) | ||
| .getConfigurationAsJson(eq("/v1/subaccountDestinations"), any()); | ||
|
|
||
| DestinationService.Cache.setGetAllDocumentsPrepended(true); | ||
|
|
||
| final DestinationOptions options = | ||
| DestinationOptions.builder().augmentBuilder(augmenter().retrievalStrategy(ALWAYS_PROVIDER)).build(); | ||
| assertThatThrownBy(() -> loader.tryGetDestination("thisDestinationDoesNotExist").get()) | ||
| .isInstanceOf(DestinationAccessException.class) | ||
| .hasMessageContaining("was not found among the destinations of the current tenant."); | ||
|
|
||
| // verify all results are cached | ||
| verify(destinationServiceAdapter, times(1)).getConfigurationAsJson(eq("/v1/instanceDestinations"), any()); | ||
| verify(destinationServiceAdapter, times(1)).getConfigurationAsJson(eq("/v1/subaccountDestinations"), any()); | ||
| verifyNoMoreInteractions(destinationServiceAdapter); | ||
|
|
||
| DestinationService.Cache.setGetAllDocumentsPrepended(false); | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.