Netflix / Hystrix

Hystrix is a latency and fault tolerance library designed to isolate points of access to remote systems, services and 3rd party libraries, stop cascading failure and enable resilience in complex distributed systems where failure is inevitable.
24.13k stars 4.71k forks source link

List of objects cause fallback to throw incompatible return types exception #1856

Open bentein opened 6 years ago

bentein commented 6 years ago

I have the following Hystrix fallback

public List<Data> getDataFallback(String id, LocalDate startDate, LocalDate endDate, Throwable t) {
    log.warn("Could not get data");
    throw new DataException(ErrorMessage.builder()
            .code(COULD_NOT_GET_DATA.name())
            .internalMessage(t.getMessage())
            .build());
}

Which is attached to the following method

@Override
@HystrixCommand(fallbackMethod = "getDataFallback") 
public List<Data> getData(String id, LocalDate startDate, LocalDate endDate) {
    return repository.find(id, startDate, endDate);
}

The Data object is a simple POJO, using Lombok to generate methods, and looks like this

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Entity
public class Data {

    @EmbeddedId
    private Foo foo;

    private Bar bar;
}

When the annotated method is run directly from my Spring controller, I get the following error:

com.netflix.hystrix.contrib.javanica.exception.FallbackDefinitionException: Incompatible return types. 
Command method: public java.util.List package.class.getData(java.lang.String,java.time.LocalDate,java.time.LocalDate);
Fallback method: public java.util.List package.class.getDataFallback(java.lang.String,java.time.LocalDate,java.time.LocalDate,java.lang.Throwable);
Hint: Different size of types variables.
Command  type literals size = 2: [java.util.List<package.Data>, class package.Data]
Fallback type literals size = 1: [interface java.util.List]

Command type literal pos: unknown; Fallback type literal pos: unknown

at com.netflix.hystrix.contrib.javanica.utils.FallbackMethod.validateReturnType(FallbackMethod.java:180)
at com.netflix.hystrix.contrib.javanica.utils.FallbackMethod.validateReturnType(FallbackMethod.java:138)
at com.netflix.hystrix.contrib.javanica.aop.aspectj.HystrixCommandAspect.setFallbackMethod(HystrixCommandAspect.java:344)

If I wrap the list in another object, and refactor the methods accordingly, the exact same configuration works as expected.

When calling the annotated method inside another method, example below, instead of calling it directly from the controller, the exact same configuration works as expected.

@Override
public List<Data> getData(String id, LocalDate startDate, LocalDate endDate) {
    return getDataInternal(id, startDate, endDate);
}

@HystrixCommand(fallbackMethod = "getDataFallback") 
private List<Data> getDataInternal(String id, LocalDate startDate, LocalDate endDate) {
    return repository.find(id, startDate, endDate);
}
abdel777 commented 5 years ago

did you find any solution ? , it seems i have the same problem

bentein commented 5 years ago

Did you try to create an internal private method, and add the annotation to that method instead? That was one way to solve it. Another is to simply create a wrapper-class for the list, and use that as the return type instead.

cengozge commented 5 years ago

@bentein You need to have the same parameters with your method for the fallback method. This is what the netflix documentation says and also in stacktrace it gives as a hint "Hint: Different size of types variables." It seems a bit strange relation of return type and parameters.

Your method is public List getData(String id, LocalDate startDate, LocalDate endDate) Your fallback is public List getDataFallback(String id, LocalDate startDate, LocalDate endDate, Throwable t)

Fallback has additional parameter. You need to decide that point.