spring-cloud / spring-cloud/spring-cloud-openfeign

FeignHttpMessageConverters concurrency issue

Open
#1,307 12 comments 28 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

waiting-for-triage
Dominant language
Java
Stars
1.3k
Forks
838
Avg merge
2d 11h
Merged PRs (30d)
13

Description

Describe the bug
When mutiple concurrent threads call the org.springframework.cloud.openfeign.support.FeignHttpMessageConverters#getConverters method on a non-initialized class, some non-initialized converters could be returned. This leads to similar exceptions when processing requests/responses. After the class is initialized, no exceptions are thrown.

feign.codec.EncodeException: Could not write request: no suitable HttpMessageConverter found for request type [com.MyClass]
	at org.springframework.cloud.openfeign.support.SpringEncoder.encodeWithMessageConverter(SpringEncoder.java:168)
	at org.springframework.cloud.openfeign.support.SpringEncoder.encode(SpringEncoder.java:110)
	at org.springframework.cloud.openfeign.support.PageableSpringEncoder.encode(PageableSpringEncoder.java:105)
	at feign.RequestTemplateFactoryResolver$BuildEncodedTemplateFromArgs.resolve(RequestTemplateFactoryResolver.java:274)
	at feign.RequestTemplateFactoryResolver$BuildTemplateByResolvingArgs.create(RequestTemplateFactoryResolver.java:107)
	at feign.SynchronousMethodHandler.invoke(SynchronousMethodHandler.java:48)
	at feign.ReflectiveFeign$FeignInvocationHandler.invoke(ReflectiveFeign.java:104)
	at org.springframework.cloud.openfeign.FeignCachingInvocationHandlerFactory$1.proceed(FeignCachingInvocationHandlerFactory.java:66)
	at org.springframework.cache.interceptor.CacheInterceptor.lambda$invoke$0(CacheInterceptor.java:54)
	at org.springframework.cache.interceptor.CacheAspectSupport.invokeOperation(CacheAspectSupport.java:424)
	at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:410)
	at org.springframework.cache.interceptor.CacheInterceptor.invoke(CacheInterceptor.java:64)
	at org.springframework.cloud.openfeign.FeignCachingInvocationHandlerFactory.lambda$create$0(FeignCachingInvocationHandlerFactory.java:53)
	at jdk.proxy2/jdk.proxy2.$Proxy166.list(Unknown Source)
	... 189 filtered

java.lang.IllegalArgumentException: 'messageConverters' must not contain null elements
	at org.springframework.util.Assert.noNullElements(Assert.java:436)
	at org.springframework.web.client.HttpMessageConverterExtractor.<init>(HttpMessageConverterExtractor.java:78)
	at org.springframework.web.client.HttpMessageConverterExtractor.<init>(HttpMessageConverterExtractor.java:71)
	at org.springframework.cloud.openfeign.support.SpringDecoder.decode(SpringDecoder.java:56)
	at org.springframework.cloud.openfeign.support.ResponseEntityDecoder.decode(ResponseEntityDecoder.java:62)
	at feign.optionals.OptionalDecoder.decode(OptionalDecoder.java:38)
	at feign.InvocationContext.decode(InvocationContext.java:121)
	... 201 more
Wrapped by: <#64f634dc> feign.codec.DecodeException: 'messageConverters' must not contain null elements
	at feign.InvocationContext.decode(InvocationContext.java:125)
	at feign.InvocationContext.proceed(InvocationContext.java:94)
	at feign.ResponseHandler.handleResponse(ResponseHandler.java:69)
	at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:109)
	at feign.SynchronousMethodHandler.invoke(SynchronousMethodHandler.java:53)
	at feign.ReflectiveFeign$FeignInvocationHandler.invoke(ReflectiveFeign.java:104)
	at org.springframework.cloud.openfeign.FeignCachingInvocationHandlerFactory$1.proceed(FeignCachingInvocationHandlerFactory.java:66)
	at org.springframework.cache.interceptor.CacheInterceptor.lambda$invoke$0(CacheInterceptor.java:54)
	at org.springframework.cache.interceptor.CacheAspectSupport.invokeOperation(CacheAspectSupport.java:424)
	at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:410)
	at org.springframework.cache.interceptor.CacheInterceptor.invoke(CacheInterceptor.java:64)
	at org.springframework.cloud.openfeign.FeignCachingInvocationHandlerFactory.lambda$create$0(FeignCachingInvocationHandlerFactory.java:53)
	at jdk.proxy2/jdk.proxy2.$Proxy166.list(Unknown Source)
	... 172 filtered

Workaround

Current workaround is declaring own converters bean and call initialization explicitly at application startup:

    @Bean
    @ConditionalOnMissingBean
    public FeignHttpMessageConverters feignHttpMessageConverters(
            ObjectProvider<HttpMessageConverter<?>> messageConverters,
            ObjectProvider<HttpMessageConverterCustomizer> customizers) {
        var feignHttpMessageConverters = new FeignHttpMessageConverters(messageConverters, customizers);
        // init converters
        feignHttpMessageConverters.getConverters();
        return feignHttpMessageConverters;
    }

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reading FeignHttpMessageConverters#getConverters and the constructor usage shown in the workaround. Reproduce concurrent calls before initialization and inspect whether incomplete or null converters can be returned. Done means concurrent request and response processing no longer produces the reported converter exceptions, with coverage for initialization under concurrency.

Written by the indexing model from the issue text.

Assessment

Tech stack
java, spring
Domain
api, backend
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
57/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.