Q: Using Circuit Breaker in SAP Cloud SDK resilience - connectivity

When I tried to use ResilienceDecorator.executeCallable() to enable circuit breaker, I have to throw out ResilienceRuntimeException in my callable to make the circuit break work. Sample code as below. Without it, circuit breaker is always closed. is this the right way to do it?
response = ResilienceDecorator.executeCallable(() -> {
HttpResponse response1 = tryHttpClient.get().execute(request);
if (response1.getStatusLine().getStatusCode() == 404){
throw new ResilienceRuntimeException("404 error is raised when calling SB api");
}
return response1;
},
ResilienceConfiguration.of(SubscriptionBillingAdapter.class).isolationMode(ResilienceIsolationMode.TENANT_OPTIONAL).timeLimiterConfiguration(ResilienceConfiguration.TimeLimiterConfiguration.of().timeoutDuration(Duration.ofSeconds(6L))).circuitBreakerConfiguration(ResilienceConfiguration.CircuitBreakerConfiguration.of().waitDuration(Duration.ofSeconds(600000L)).failureRateThreshold(1).closedBufferSize(1).halfOpenBufferSize(1)),
e -> {LOG.warn("resiliience fallback call: " + e); return response1;});
I am asking since I don't see any document of it. Also when I check how destination configuration in SCP is retrieved, I saw the following code in com.sap.cloud.sdk.cloudplatform.connectivity.DestinationService . It doesn't throw out ResilienceRuntimeException, when using ResilienceDecorator.executeCallable(). so my question is do I need to throw out ResilienceRuntimeException or not to make circuit breaker work? if I don't need, anything wrong in my code?
return (String)ResilienceDecorator.executeCallable(() -> {
XsuaaCredentials xsuaaCredentials = (new ServiceCredentialsRetriever()).getClientCredentials("destination");
AccessToken accessToken;
if (propagateUser) {
accessToken = xsuaaService.retrieveAccessTokenViaUserTokenExchange(xsuaaCredentials.getXsuaaUri(), xsuaaCredentials.getCredentials(), useProviderTenant);
} else {
accessToken = xsuaaService.retrieveAccessTokenViaClientCredentialsGrant(xsuaaCredentials.getXsuaaUri(), xsuaaCredentials.getCredentials(), useProviderTenant);
}
return this.fetchDestinationsJson(servicePath, accessToken);
}, ResilienceConfiguration.of(DestinationService.class).isolationMode(ResilienceIsolationMode.TENANT_OPTIONAL).timeLimiterConfiguration(TimeLimiterConfiguration.of().timeoutDuration(Duration.ofSeconds(6L))).circuitBreakerConfiguration(CircuitBreakerConfiguration.of().waitDuration(Duration.ofSeconds(6L))));

Steven
I'm not the most experienced in this specific part, but looking at your code it seems fine to me. When a server returns 404 Not found it's not an indication of a service failure or error, but that resource is simply not found. If in your case 404 means that an error took place and the request has to be retried with a resilient approach, you have to throw that exception to inform Resilience4J that smth went wrong.
While we're working on improving our documentation, I recommend you take a look at the existing tutorial explaining resilience within the SAP Cloud SDK context. There we also throw ResilienceRuntimeException for clarity:
public List<BusinessPartner> execute() {
return ResilienceDecorator.executeSupplier(this::run, myResilienceConfig, e -> {
logger.warn("Fallback called because of exception.", e);
return Collections.emptyList();
});
}
private List<BusinessPartner> run() {
try {
return businessPartnerService
.getAllBusinessPartner()
.select(BusinessPartner.BUSINESS_PARTNER,
BusinessPartner.LAST_NAME,
BusinessPartner.FIRST_NAME,
BusinessPartner.IS_MALE,
BusinessPartner.IS_FEMALE,
BusinessPartner.CREATION_DATE,
BusinessPartner.TO_BUSINESS_PARTNER_ADDRESS
.select(BusinessPartnerAddress.CITY_NAME,
BusinessPartnerAddress.COUNTRY,
BusinessPartnerAddress.TO_EMAIL_ADDRESS
.select(AddressEmailAddress.EMAIL_ADDRESS)
)
)
.filter(BusinessPartner.BUSINESS_PARTNER_CATEGORY.eq(CATEGORY_PERSON))
.orderBy(BusinessPartner.LAST_NAME, Order.ASC)
.top(200)
.execute(destination);
} catch (ODataException e) {
throw new ResilienceRuntimeException(e);
}
}
Regarding the code snippet from the DestinationService, I believe that fetchDestinationsJson() method throws an implicit exception thus letting Resilience4J know that smth went wrong. While in your case HttpClient won't throw anything when receiving 404 as it's a correct response code as any other.
I also think that checking CircuitsBreaker examples from Resilience4J library might be helpful.
I hope it helps:)

No you do not have to throw a ResilienceRuntimeException. In fact the SDK only uses that to wrap checked and unchecked exceptions into an unchecked exception which wraps all kinds of failures that occur within a resilient call.
Please expand your question with more details, I'll then expand this answer:
Which version of the SDK are you using?
How (and how often) do you invoke the decorated callable? Please expand the code block.
Please specify the exact behaviour you observe. What exception is thrown when you invoke the decorated callable? If the circuit breaker opens this should be a CallNotPermittedException wrapped inside a ResilienceRuntimeException
Reduce the callable to simply throw an exception in order to simplify the code
Reduce the resilience configuration to only use a circuit breaker (leverage ResilienceConfiguration.empty()). If that works add stuff back in again until it doesn't.
For reference also please find the documentation of resilience4j which the SDK uses under the hood to perform resilient operations.

Related

How do I correctly implement dart future.catcherror? [duplicate]

I have some code like this:
File("foo.txt").readAsString().catchError((e)=>print(e));
The compiler is complaining
info: The return type 'void' isn't assignable to 'FutureOr<T>', as required by 'Future.catchError'.
I can't seem to give it what it wants and can't find a single clear usage example in any of the docs (just a long issue in git about how many ways there are to mis-use this). If I take the docs at face value, I should be able to return a bool, or a future, neither make the analyzer happy.
How do I provide this FutureOr?
The documentation for Future.catchError could be a lot clearer, but the relevant part is:
onError is called with the error and possibly stack trace, and the returned future is completed with the result of this call in exactly the same way as for then's onError.
Cross-referencing to the documentation for Future.then, the relevant portion is:
The onError callback must return a value or future that can be used to complete the returned future, so it must be something assignable to FutureOr<R>.
Since File.readAsString returns a Future<String>, your catchError callback also must return a Future<String>. Examples of doing that:
File("foo.txt").readAsString().catchError((e) {
print(e);
return Future.value('');
});
File("foo.txt").readAsString().catchError((e) async {
print(e);
return '';
});
Logically, this makes sense; because given:
String value = await File("foo.txt").readAsString().catchError(...);
then if readAsString succeeds, value should be assigned a String. If it fails, since you catch the exception without rethrowing it, value still needs to be assigned a String.
Put another way, your code is equivalent to:
Future<String> readFoo() async {
try {
return await File("foo.txt").readAsString();
} catch (e) {
print(e);
}
// Oops, missing return value.
}
In general, I strongly recommend using async/await with try-catch instead of using .catchError, which would avoid this confusion.

Xamarin Forms - iOS not processing correctly

I am using the CrossDownManager plugin for Xamarin Forms
Here
When I run the method on Android it processes as expected. On iOS Debug.Writeline("Success!") isn't being hit like it was on Android.
Here is the code:
void ViewImage(string imageLink)
{
var downloadManager = CrossDownloadManager.Current;
downloadManager.PathNameForDownloadedFile = new System.Func<IDownloadFile, string>(file =>
{
string path = DependencyService.Get<IImageSaver>().Save("YHTS" + DateTime.Today.Ticks.ToString() + ".jpg");
Debug.WriteLine("Success!");
return path;
});
try
{
var file = downloadManager.CreateDownloadFile(imageLink);
Debug.WriteLine("file created");
downloadManager.Start(file);
Debug.WriteLine("downloadstarted");
}
catch (Exception e)
{
Debug.WriteLine(e.ToString());
}
}
For the life of me I can't figure out why the that code block isn't processed. Any ideas?
This is an interesting issue as technically your code should work as expected. I've done a little digging and found a reply to a similar question here.
your options are many... including:
DEBUG preprocessor as you show in your question.
Use System.Diagnostic.Debug.WriteLine: Any calls to Debug.* will be
removed by the compiler due to the [Conditional("DEBUG")] attribute
being applied.
Create your own "Logger" class as a wrapper to the stdout writers and
[Conditional("DEBUG")] it
Use Fody and re-weave the assemblies to remove/NOP/redirect the
WriteLine I do this to redirect the calls to in internal log and upon
crash or user stat requests, forward this log to our crash reporting
servers. etc, .....
So there are a few alternatives to consider, one of the common suggestions I've seen is to use the fully qualified reference for WriteLine(); as such:
System.Console.WriteLine("woop woop");
I would suggest giving the above a try first.

How to handle errors while using Glib.Settings in Vala?

I am using Glib.Settings in my Vala application. And I want to make sure that my program will work okay even when the schema or key is not available. So I've added a try/catch block, but if I'm using the key that doesn't exist, the program segfaults. As I understood, it doesn't even reach the catch statement.
Here is the function that uses settings:
GLib.Settings settings;
string token = "";
try
{
settings = new GLib.Settings (my_scheme);
token = settings.get_string("token1");
}
catch (Error e)
{
print("error");
token = "";
}
return token;
And the program output is:
(main:27194): GLib-GIO-ERROR **: Settings schema 'my_scheme' does not contain a key named 'token1'
Trace/breakpoint trap (core dumped)
(of course I'm using my real scheme string instead of my_scheme)
So can you suggest me where I'm wrong?
I know this is super late, but I was looking for the same solution so I thought I'd share one. As #apmasell said, the GLib.Settings methods don't throw exceptions—they just abort instead.
However, you can do a SettingsSchemaSource.lookup to make sure the key exists first. You can then also use has_key for specific keys. For example,
var settings_schema = SettingsSchemaSource.get_default ().lookup ("my_scheme", false);
if (settings_schema != null) {
if (settings_schema.has_key ("token1")) {
var settings = new GLib.Settings ("my_scheme");
token = settings.get_string("token1");
} else {
critical ("Key does not exist");
}
} else {
critical ("Schema does not exist");
}
The methods in GLib.Settings, including get_string do not throw exceptions, they call abort inside the library. This is not an ideal design, but there isn't anything you can do about it.
In this case, the correct thing to do is fix your schema, install into /usr/share/glib-2.0/schemas and run glib-compile-schemas on that directory (as root).
Vala only has checked exceptions, so, unlike C#, a method must declare that it will throw, or it is not possible to do so. You can always double check the Valadoc or the VAPI to see.

May a scenario not have a When in BDD?

I'm currently learning/testing BDD using SpecFlow, and it works great!
Before I choose to ask my question, I have read this one, and I felt like I had to ask my question despite the fact that the same problem is addressed, because of the Exception scenario which is not mentioned.
I'm actually testing this scenario:
Scenario: I should get an error whenever I try to remove an item from an empty stack
Given I have an empty stack
When I pop from it
Then I should get an error
public class StackBehaviour {
public void GivenIHaveAnEmptyStack() { stack = new CustomStack<string>(); }
// This will throw whenever called!
// So the Then method will never be run!
// I feel like I should just put a comment which says why it's empty,
// allowing a fellow programmer to understand the exact intention.
public void WhenIPopFromIt() { stack.Pop(); }
// It is here that it verifies whether the CustomStack meets the expected behaviour.
public void ThenIShouldGetAnError() {
Assert.Throws<IndexOutOfRangeException>(delegate {
stack.Pop();
});
}
private CustomStack<string> stack;
}
public class CustomStack<T> {
public T Pop() {
if (stack.Count == 0)
throw new IndexOutOfRangeException("Cannot pop from an empty stack!");
T item = stack[stack.Count-1];
stack.RemoveAt(stack.Count-1);
return item;
}
private ArrayList stack = new ArrayList();
}
I think that leaving a comment in the When method is correct, so that the business requirement doesn't lack any information, and on the code behind, I put it clear what my intention is exactly by commenting.
What do you think? Any other ideas why I shouldn't make it?
There is another trick that you can use where the bindings help make the feature's meaning a little clearer. In this case, let's start at the end.
Then I should get an error
Your problem is a basically here, you know want an error, but you don't know how to get it. In fact you've already missed it, the error has already occurred in the When step, and in your code example, you moved the action into the then step just so you could get the error.
But what if keep the when performing the action amd re-express our then to reflect what really happens
Then I should have had an error
Seems a trivial change but now our feature reflects that the error should have been associated with the When step and we are simply evaluating it later, and that is something we can code. We just need something to remember the error in the when and deliver it to the then.
private Exception errorFromWhen = null;
public void WhenIPopFromIt()
{
try
{
stack.Pop();
}
catch(Exception ex)
{
errorFromWhen = ex;
}
}
public void ThenIShouldGetAnError()
{
errorFromWhen.ShouldNotBeNull();
errorFromWhen.ShouldBe<IndexOutOfRangeException>();
}
SpecFlow has absolutely no problems with this, in fact due to its mini Dependency injection system, you can even pass this state between binding classes if necessary.
May a scenario not have a When in BDD?
In Specflow, neither given, when or then are mandatory.
However in your example, I don't believe this is a good use of Specflow and BDD. In this answer here Marcus states:
"BDD is about ensuring that we're building the right thing, TDD is about ensuring that we're building it right."
In your example the scope of what is being tested i.e. the CustomStack, should be tested via TDD. It is the end solution that makes use of the CustomStack should be tested via BDD (and hence SpecFlow) e.g. if this CustomStack was being exercised via a certain action on a website.
This answer is also pertinent to your question.

Is this a valid way to code up an MVC Service Layer?

In my MVC service layer I have code such as the following to validate:
protected bool ValidateAccount(Account account)
{
var accounts = _accountRepository.GetPk(account.PartitionKey);
if (accounts.Any(b => b.Title.Equals(account.Title) &&
!b.RowKey.Equals(account.RowKey)))
_validationDictionary.AddError("", "Duplicate title");
return _validationDictionary.IsValid;
}
However in the "action type" methods I absorb exceptions with code like this:
public bool Create(Account account)
{
if (!ValidateAccount(account))
return false;
try
{
_accountRepository.AddOrUpdate(account);
}
catch
{
return false;
}
return true;
}
My controller is coded like this:
public ActionResult Create(BaseViewModel vm)
{
_accountService = new AccountService(new ModelStateWrapper(this.ModelState), vm.Meta.DataSourceID);
if (ModelState.IsValid)
{
_accountService = new AccountService(new ModelStateWrapper(this.ModelState), vm.Meta.DataSourceID);
if (!_accountService.Create(vm.Account))
return View("CreateEdit", vm);
else
return RedirectToAction("Created");
}
return RedirectToAction("Home");
}
return View("CreateEdit", vm);
}
Is this a reasonable approach to take? My one concern is that I might be losing exception information in the service layer.
You should at the very least log your exception somewhere (event log, file system, using Elmah, etc).
The problem with your code is that you'll never know if something bad happened in production environment or you won't be able to discover what failed exactly (just that the code returned false).
Plus, you should never handle all exceptions like you do (unfiltered catch) but only the one you can revert back to a normal state. It's far better to let the application crash than to keep it online unstable.
Avoid handling errors by catching non-specific exceptions, such as System.Exception, System.SystemException, and so on, in application code. There are cases when handling errors in applications is acceptable, but such cases are rare.
See this MSDN article on best practices for handling exceptions and Design Guidelines for Exceptions
Since you are not doing anything with the exception in your Create method in the service layer, I would suggest removing the try/catch from it.
I would still return true or false to indicate whether the create operation was successful. I will however add a try/catch in the caller to make sure exceptions are handled. In your case that could be in your controller action (or the OnException in your base controller.)
Another approach is to leave the try/catch in your Create method but as #Stephane suggested do something with it (like log it) but you could also log it wherever you catch it.

Resources