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

스캐빈저 agent 적용 시 SerializationFailedException 발생 문의 #125

Open
JaeHyeonKim19 opened this issue Jun 5, 2024 · 5 comments

Comments

@JaeHyeonKim19
Copy link

안녕하세요, 스캐빈저 적용 시 레디스 캐시 데이터 deserialize 할 때 SerializationFailedException가 발생하여 관련 내용 문의드립니다.

배경 지식 공유

현재 프로젝트(spring boot)에서 레디스 캐시를

  1. Serializer는 JdkSerializationRedisSerializer를 사용
  2. 캐시되는 클래스는 Serializable 인터페이스 상속 중
  3. 여러 대의 서버가 캐시 목적으로 단일 레디스 서버 사용 중

와 같은 환경에서 사용 중 입니다.

에러 상황

스캐빈저 agent 적용 후 배포 시 SerializationFailedException에러 발생 (stack trace는 본문 마지막에 첨부 드리겠습니다.)

문의 사항

  1. 스캐빈저 소개 PDF를 보면 'byte code 수정을 통해 메소드 실행 전/후 코드를 주입하고 있다'고 되어있는데요, 이로인해 스캐빈저 적용 전/후 동일 클래스에서 jvm에서 생성하는 serialVersionUID가 변경될 가능성이 있을까요?
  2. 실행 환경에 따라 조작되는 byte code 값이 다를 수 있나요?
    1. 개발환경, 로컬환경에서 테스트 했을 때 환경별로 에러 발생 유무가 다른 경우가 있어 질문드립니다.
  3. 여러 대의 서버가 떠 있을 경우 각 서버에서 조작되는 byte code 값이 다를 수 있나요?
    1. 위 2번 질문에 답변이 '예'라면 동일 개발환경에서도 별도 인스턴스라면 조작되는 byte값이 다를 수 있지 않을까하여 질문드립니다. 메소드 실행 전/후 코드 주입 시 각 인스턴스의 고유값이 포함되는 경우 등에 byte code값이 다르게 조작될 수 있다고 생각했습니다. 또한 이 경우 각 클래스의 serialVersionUID 값 또한 다르게 생성 될 수 있을 것으로 보이고 각 다른 서버에서 cache를 write/read 할 경우 동일 에러가 발생될 가능성이 있다고 생각됩니다.

(참고사항) 현재 생각중인 해결 방안

스캐빈저 설정 옵션 중 annotations 옵션을 사용하여 캐시에 사용되는 dto들은 스캔범위에서 제외하고자 합니다.
(레디스 serializer를 변경해서 해결할 수 도 있지만 해당 방식은 영향범위가 클것으로 예상되어 serializer 수정없이 해결하고자 합니다.)

에러 상세 stacktrace

사내 프로젝트라 일부 정보(패키지 경로 등)는 삭제 또는 수정하였습니다. 참고 부탁드립니다.


org.springframework.data.redis.serializer.SerializationException: Cannot deserialize; nested exception is org.springframework.core.serializer.support.SerializationFailedException: Failed to deserialize payload. Is the byte array a result of corresponding serialization for DefaultDeserializer?; nested exception is java.io.InvalidClassException: com.�abc.dto.GoodsDto; local class incompatible: stream classdesc serialVersionUID = -2606788700877345633, local class serialVersionUID = -6040559779750867146
	at org.springframework.data.redis.serializer.JdkSerializationRedisSerializer.deserialize(JdkSerializationRedisSerializer.java:84)
	at org.springframework.data.redis.serializer.DefaultRedisElementReader.read(DefaultRedisElementReader.java:48)
	at org.springframework.data.redis.serializer.RedisSerializationContext$SerializationPair.read(RedisSerializationContext.java:272)
	at org.springframework.data.redis.cache.RedisCache.deserializeCacheValue(RedisCache.java:260)
	at org.springframework.data.redis.cache.RedisCache.lookup(RedisCache.java:94)
	at org.springframework.cache.support.AbstractValueAdaptingCache.get(AbstractValueAdaptingCache.java:58)
	at org.springframework.cache.transaction.TransactionAwareCacheDecorator.get(TransactionAwareCacheDecorator.java:80)
	at org.springframework.cache.interceptor.AbstractCacheInvoker.doGet(AbstractCacheInvoker.java:73)
	at org.springframework.cache.interceptor.CacheAspectSupport.findInCaches(CacheAspectSupport.java:555)
	at org.springframework.cache.interceptor.CacheAspectSupport.findCachedItem(CacheAspectSupport.java:520)
	at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:402)
	at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:346)
	at org.springframework.cache.interceptor.CacheInterceptor.invoke(CacheInterceptor.java:61)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749)
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:691)
	at com.abc.mapper.GoodsDtoMapper$$EnhancerBySpringCGLIB$$b61f6dcf.map(<generated>)
	at com.abc.service.GoodsService.findById(GoodsService.java:12)
	at com.abc.service.GoodsService$$FastClassBySpringCGLIB$$6375fce8.invoke(<generated>)
	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:771)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749)
	at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:367)
	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:118)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749)
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:691)
	at com.abc.service.GoodsService$$EnhancerBySpringCGLIB$$47b5e4cf.findById(<generated>)
	at com.abc.controller.GoodsApiController.findGoods(GoodsApiController.java:58)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190)
	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138)
	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:105)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:878)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:792)
	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040)
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943)
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
	at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:626)
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:733)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at com.abc.configuration.filter.Filter.doFilterInternal(Filter.java:28)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at com.abc.configuration.filter.Filter2.doFilterInternal(Filter2.java:37)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at com.abc.configuration.filter.Filter3.doFilterInternal(Filter3.java:50)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.web.servlet.v3_1.OpenTelemetryHandlerMappingFilter.doFilter(OpenTelemetryHandlerMappingFilter.java:83)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:93)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:373)
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868)
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1589)
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
	at java.base/java.lang.Thread.run(Thread.java:829)
Caused by: org.springframework.core.serializer.support.SerializationFailedException: Failed to deserialize payload. Is the byte array a result of corresponding serialization for DefaultDeserializer?; nested exception is java.io.InvalidClassException: com.abc.goods.dto.GoodsDto; local class incompatible: stream classdesc serialVersionUID = -2606788700877345633, local class serialVersionUID = -6040559779750867146
	at org.springframework.core.serializer.support.DeserializingConverter.convert(DeserializingConverter.java:78)
	at org.springframework.core.serializer.support.DeserializingConverter.convert(DeserializingConverter.java:36)
	at org.springframework.data.redis.serializer.JdkSerializationRedisSerializer.deserialize(JdkSerializationRedisSerializer.java:82)
	... 98 common frames omitted
Caused by: java.io.InvalidClassException: com.abc.dto.GoodsDto; local class incompatible: stream classdesc serialVersionUID = -2606788700877345633, local class serialVersionUID = -6040559779750867146
	at java.base/java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:560)
	at java.base/java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:2020)
	at java.base/java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1870)
	at java.base/java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:2020)
	at java.base/java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1870)
	at java.base/java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2201)
	at java.base/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1687)
	at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:489)
	at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:447)
	at org.springframework.core.serializer.DefaultDeserializer.deserialize(DefaultDeserializer.java:72)
	at org.springframework.core.serializer.support.DeserializingConverter.convert(DeserializingConverter.java:73)
	... 100 common frames omitted
@sohyun-ku
Copy link
Contributor

@JaeHyeonKim19

안녕하세요.
scavenger-agent의 instrument 과정에서 serialVersionUID이 변경된 것으로 보입니다.
아래 이슈와 관련이 있어 보이며 제보해주신 이슈는 패치하여 다음 릴리즈에 포함하도록 하겠습니다.
우선 말씀해주신 것 처럼 직렬화가 필요한 DTO 클래스는 제외 후에 사용 부탁드리겠습니다. ( _ _ )

raphw/byte-buddy#167

@JaeHyeonKim19
Copy link
Author

안녕하세요 @sohyun-ku 님, 빠른 답변 감사합니다.
혹시 다음 릴리즈 시기는 언제쯤인지 대략적으로 알 수 있을까요?

@taeyeon-Kim
Copy link
Contributor

taeyeon-Kim commented Jun 5, 2024

@sohyun-ku
https://stackoverflow.com/questions/54163653/override-doesnt-work-when-using-bytebuddy-disableclassformatchanges
를 보면 disableClassFormatChanges 옵션 때문에 예상치 못한 이슈가 더 발생할 수 도 있을 것 같습니다.
또.. bytebuddy로 인해 SerialVersionUID가 바뀌나 테스트해보니까 그런 것 같지도 않네요.. 일단 상황을 재연해보는게 먼저 일 것 같습니다 😭

@sohyun-ku
Copy link
Contributor

@JaeHyeonKim19
이슈 재연 및 기능 적용 시 영향도 확인이 필요해 릴리즈 시점을 공유드리기 어렵습니다.
로컬 환경에서 제보해 주신 이슈에 대해서 재연이 되지 않는 상황이라 확인이 더 힘든 상황입니다.
이슈 확인을 위해 사용하시는 환경 설명 및 재연이 되는 상황을 샘플 프로젝트와 같은 형태로 제공해 주실 수 있으실까요?

@JaeHyeonKim19
Copy link
Author

네, 확인해보고 공유드리겠습니다.

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

No branches or pull requests

3 participants