Skip to content
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

Support for single fallback method with method arguments not matching #1446

Closed
angelom88 opened this issue Dec 16, 2016 · 20 comments
Closed
Labels

Comments

@angelom88
Copy link

angelom88 commented Dec 16, 2016

I am thinking if we could implement a single fallback for all methods annotated with @HystrixCommand regardless of its method parameter arguments. Since for now every endpoint method must matched to its corresponding fallback method which is dependent on parameter arguments. It sounds so redundant and untidy code if we keep having same functionality for all those fallback methods. Another option is global or generic fallback added to @DefaultProperties attribute.

@RequestMapping(value = "/test1")
@HystrixCommand(fallbackMethod = "fallback") 
public APIResponse test1(String param1) {
	// some codes here
	return APIResponse.success("success");
}

@RequestMapping(value = "/test2")
@HystrixCommand(fallbackMethod = "fallback")
public APIResponse test2() {
	// some codes here
	return APIResponse.success("success");
}

@RequestMapping(value = "/test3")
@HystrixCommand(fallbackMethod = "fallback")
public APIResponse test3(ObjectRequest obj) {
	// some codes here
	return APIResponse.success("success");
}

private APIResponse fallback() {
	return APIResponse.failed("Server is busy");
}
@mattrjacobs
Copy link
Contributor

This seems like a special-case which we haven't encountered of all methods returning the same type and wanting the same fallback. Anyone in the community also run into this case and feel like this feature is missing?

@angelom88
Copy link
Author

angelom88 commented Jan 3, 2017

@mattrjacobs it was also needed by someone several months back Spring Hystrix Single Fallback with Method Arguments Not Matching .

@mattrjacobs
Copy link
Contributor

We don't use the Javanica method of interacting with Hystrix internally, so this is not a feature we're looking to build. If you want to build it yourself, then can you coordinate with @dmgcodevil ?

@iwin4aids
Copy link

iwin4aids commented Feb 10, 2017

This feature I think is really needed~ Can there be an common fallback method just simplely return null for all methods?

@dmgcodevil
Copy link
Contributor

dmgcodevil commented Feb 14, 2017

Hi folks, it's possible. There are two options:

  1. Add new annotation
@DefaultFallback
Object defaultFallback() {
 return null;
}
  1. Add new property in @DefaultProperties : defaultFallback="defaultFallback"

I think that the second option is better, wdyt ?

@angelom88
Copy link
Author

@dmgcodevil I'll go for second option. :)
Thanks in advance. :)

@dmgcodevil
Copy link
Contributor

I'm thinking about defaultFallback return type: should it be Object, void or a concrete type? My understanding is that if defaultFallback can be applied for any command in the given class then a command return type should be compatible with defaultFallback one otherwise it will fail at runtime with "ClassCastException", e.g. :

@DefaultProperties(defaultFallback="fallback")
class Service {
  @HystrixCommand    
   String foo(int i) {} // -> return type matches fallback one
   
   Integer bar(int i) {} // -> fails at runtime with ClassCast     

   String fallback(){} // -> concrete return type
}

Using Object as fallback return type doesn't solve the problem either, it's even more error prone.
I think that this approach restricts an user in having commands with different return types and default fallback in the same class. Maybe it's ok, but sound brittle, isn't ?

As an alternative we can add defaultFallback in @HystrixCommand:

@HystrixCommand(defaultFallback="stringFallback")
String commandX(int a)

@HystrixCommand(defaultFallback="longFallback")
Long commandY(int a, int b)

@HystrixCommand(defaultFallback="longFallback")
Long commandZ(int a, int b, int c)

String stringFallback() {}
Long longFallback() {}

or to @DefaultProperties but defaultFallback return type should be void

@DefaultProperties
class Service {
    @HystrixCommand
     String commandX(int a)

    @HystrixCommand
    Long commandY(int a, int b)

    void fallback() {}
}

Thoughts ?

@angelom88
Copy link
Author

In my thoughts if the developer uses the defaultFallback in @DefaultProperties he must know that all the return types of those annotated @HystrixCommand methods must be the same because he use it as a general fallback of that class, otherwise he needs to use the scope defaultFallback attribute of @HystrixCommand which must point to another fallback method. So if we restrict to only void return type I can say that it might be useless since most of the methods or function are returning any type of Object as a response dynamically.
So I imho the developer must know on how to use this kind of feature.

@dmgcodevil
Copy link
Contributor

@angelom88 so your suggestion is to add defaultFallback to @DefaultProperties and @HystrixCommand, am I right ?

@angelom88
Copy link
Author

Yes correct, so developer is free which defaultFallback scope he will be using.
Many thanks. :)

@dmgcodevil
Copy link
Contributor

dmgcodevil commented Feb 21, 2017

Created PR
BTW: do you see an error in the following code ? :

    @DefaultProperties(defaultFallback = "classDefaultFallback")
    public static class Service {
        static final String DEFAULT_RESPONSE = "class_def";

        @HystrixCommand
        public String get(String str) {
            throw new RuntimeException();
        }

        // this fallback returns string
        public String classDefaultFallback() {
            return DEFAULT_RESPONSE;
        }

        @HystrixCommand(defaultFallback = "defaultFallback")
        Long command(long l){
            throw new RuntimeException();
        }

        @HystrixCommand
        Long defaultFallback(){
            return 0L;
        }
    }

@angelom88
Copy link
Author

@dmgcodevil what do you mean?

@dmgcodevil
Copy link
Contributor

dmgcodevil commented Feb 21, 2017

my bad, misspelled the question. there is an error in the above code:

@HystrixCommand
        Long defaultFallback(){
            return 0L;
        }

it's command and fallback for Long command(long l) at the same time, when it's executed it fails because return type doesn't mach class scope default fallback, which is String. It should be changed to

 @HystrixCommand(defaultFallback = "commandDefaultFallback")
        Long command(long l){
            throw new RuntimeException();
        }
@HystrixCommand(fallbackMethod = "defaultFallback")
        Long commandDefaultFallback(){ // defaultFallback -> commandDefaultFallback
            return 0L;
        }
      Long defaultFallback() {
          return 0L;       
     }

or, because this fallback never fails we can write;

@HystrixCommand(fallbackMethod="defaultFallback")
        Long defaultFallback(){
            return 0L;
        }

just noticed that while was writing tests for this feature

@angelom88
Copy link
Author

How about if it will just support defaultFallback attribute only in class scope in @DefaultProperties? But the new feature API will prioritize the fallbackMethod if it is present in @HystrixCommand.

@dmgcodevil
Copy link
Contributor

No, It's ok to have both actually. Especially as the twist with types caused by having default fallback defined in class scope. The example above just illustrates what happens if a command return type doesn't match and how it can be fixed.

@SamBhandary
Copy link

SamBhandary commented Mar 7, 2017

I am currently trying to use default fallback method from https://github.com/Netflix/Hystrix/tree/master/hystrix-contrib/hystrix-javanica#default-fallback-for-class-or-concrete-command But the jar i am using from maven repository is 1.5.9 which doesn't have the defaultfallback method. Which jar should i use in order to get this defaultfallback feature in my Hystrix Properties.

@mattrjacobs
Copy link
Contributor

I'll get a release cut tomorrow

@pratyushbansal
Copy link

@dmgcodevil any way if we can have a common default method for methods with different return types? We are currently have a lot of code repetition
Sample code -

public OwnerSideBurnResponse getOwnerSideBurnFallback(String id, String startDate, String endDate) {
return new OwnerSideBurnResponse();
}

public PaymentAuthVerifyResponse verifyChargesPreAuthFallback(String txnId) {
    PaymentAuthVerifyResponse response = new PaymentAuthVerifyResponse();
    return response;
}

public PaymentAuthVerifyResponse cancelPreAuthFallback(String txnId) {
    PaymentAuthVerifyResponse response = new PaymentAuthVerifyResponse();
    return response;
}

public PaymentAuthResponse chargePreAuthRequestFallback(PaymentAuthRequest chargeParams) {
    PaymentAuthResponse response = new PaymentAuthResponse();
    return response;
}

Also should we avoid returning empty object and return null? What do you suggest?

@sunbufu
Copy link

sunbufu commented Aug 5, 2020

Hi guys, I want to know why the default fallback method cannot have parameters. In my view, same return type is okey, but maybe we can use Object[] as parameter for the default fallback method. Please let me know 🙏

@dmgcodevil
Copy link
Contributor

@dmgcodevil any way if we can have a common default method for methods with different return types? We are currently have a lot of code repetition
Sample code -

public OwnerSideBurnResponse getOwnerSideBurnFallback(String id, String startDate, String endDate) {
return new OwnerSideBurnResponse();
}

public PaymentAuthVerifyResponse verifyChargesPreAuthFallback(String txnId) {
    PaymentAuthVerifyResponse response = new PaymentAuthVerifyResponse();
    return response;
}

public PaymentAuthVerifyResponse cancelPreAuthFallback(String txnId) {
    PaymentAuthVerifyResponse response = new PaymentAuthVerifyResponse();
    return response;
}

public PaymentAuthResponse chargePreAuthRequestFallback(PaymentAuthRequest chargeParams) {
    PaymentAuthResponse response = new PaymentAuthResponse();
    return response;
}

Also should we avoid returning empty object and return null? What do you suggest?

cc @sunbufu

I think I can change the code to accept any set of args in default fallback, i.e.:

Response  getX(int i)
Response  getY(int j, int k)
Response  getZ(String s)


//  possible default fallbacks

Response  fallback (int i) 
Response  fallback (int i, int j) 
Response  fallback (int i, int j, int k) 
Response  fallback (int i, int j, int k) 
Response  fallback (int i, int j, String s) 

however, it's fragile and tricky, you need to make sure you have a unique set of args across all command withing the same class.

another option is to pass command name and Object[] args, i.e.:

Response  fallback (String cmd, Object[]  args) {
  switch  cmd {
     case: "getX"
     case: "getY"
     case: "getZ"
} 

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

7 participants