Objectify-在单独的线程中加载子实体会产生不一致的结果

时间:2019-04-26 20:07:07

标签: google-app-engine objectify

获取对象列表。使用多线程来获取子对象并进行一些处理。收集所有结果并发送到UI。线程产生不一致的结果。有些线程会引发错误,而有些则可以正常工作。 样例代码: 实体:父母,孩子

Parent {
    Ref<Child> childKey ;
    public Child getChild(){
        return childKey.get();
    }
}

    List<Parent> parents ; //get from db
    ExecutorService executorService = Executors.newFixedThreadPool(20, ThreadManager.currentRequestThreadFactory());
            List<Future<ParentTO>> futures = new ArrayList<>();
            for (Parent parent : parents) {
                Future<ParentTO> future = executorService.submit(() -> {
                return  ObjectifyService.run(new Work<ParentTO>() {
                        public ParentTO run() {
                            parent.getChild();                      ParentTO to = new ParentTO();

                            return to;
                        }
                    });
                });

            }

错误仅在appengine和某些线程上显示。它在本地服务器上工作正常。如果我重新加载页面,我每次都会看到不同的结果。没有线程就可以正常工作。

java.util.concurrent.ExecutionException: com.googlecode.objectify.LoadException: Error loading Parent("FND1clkiTUa_lWsJow-Dxwdmadma")/Child(1): null
    at java.util.concurrent.FutureTask.report(FutureTask.java:122)
    at java.util.concurrent.FutureTask.get(FutureTask.java:206)
    at com.netkiller.erp.service.ParentService.ParentsToApprovalTOs(ParentService.java:1720)
    at com.netkiller.erp.service.ParentService.getSharedParentsUI(ParentService.java:2000)
    at com.netkiller.common.controller.ParentController.fetchSharedParents(ParentController.java:1266)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:214)
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:132)
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:749)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:690)
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:83)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:945)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:876)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:961)
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:852)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:687)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:837)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
    at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:848)
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1772)
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:108)
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1759)
    at com.netkiller.common.filter.OpenIdFilter.doFilter(OpenIdFilter.java:859)
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1759)
    at com.netkiller.common.filter.ServiceFilter.doFilter(ServiceFilter.java:100)
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1759)
    at com.netkiller.common.filter.SecureProtocolFilter.doFilter(SecureProtocolFilter.java:35)
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1759)
    at com.googlecode.objectify.ObjectifyFilter.doFilter(ObjectifyFilter.java:48)
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1759)
    at com.google.apphosting.utils.servlet.JdbcMySqlConnectionCleanupFilter.doFilter(JdbcMySqlConnectionCleanupFilter.java:60)
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1759)
    at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:582)
    at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143)
    at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:524)
    at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:226)
    at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143)
    at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:134)
    at com.google.apphosting.runtime.jetty9.ParseBlobUploadHandler.handle(ParseBlobUploadHandler.java:119)
    at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1182)
    at com.google.apphosting.runtime.jetty9.AppEngineWebAppContext.doHandle(AppEngineWebAppContext.java:183)
    at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:512)
    at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:185)
    at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1112)
    at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)
    at com.google.apphosting.runtime.jetty9.AppVersionHandlerMap.handle(AppVersionHandlerMap.java:293)
    at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:134)
    at org.eclipse.jetty.server.Server.handle(Server.java:539)
    at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:333)
    at com.google.apphosting.runtime.jetty9.RpcConnection.handle(RpcConnection.java:213)
    at com.google.apphosting.runtime.jetty9.RpcConnector.serviceRequest(RpcConnector.java:81)
    at com.google.apphosting.runtime.jetty9.JettyServletEngineAdapter.serviceRequest(JettyServletEngineAdapter.java:123)
    at com.google.apphosting.runtime.JavaRuntime$RequestRunnable.dispatchServletRequest(JavaRuntime.java:692)
    at com.google.apphosting.runtime.JavaRuntime$RequestRunnable.dispatchRequest(JavaRuntime.java:655)
    at com.google.apphosting.runtime.JavaRuntime$RequestRunnable.run(JavaRuntime.java:625)
    at com.google.apphosting.runtime.JavaRuntime$NullSandboxRequestRunnable.run(JavaRuntime.java:817)
    at com.google.apphosting.runtime.ThreadGroupPool$PoolEntry.run(ThreadGroupPool.java:269)
    at java.lang.Thread.run(Thread.java:748)
Caused by: com.googlecode.objectify.LoadException: Error loading Parent("FND1clkiTUa_lWsJow-Dxwdmadma")/Child(1): null
    at com.googlecode.objectify.impl.EntityMetadata.load(EntityMetadata.java:78)
    at com.googlecode.objectify.impl.LoadEngine.load(LoadEngine.java:185)
    at com.googlecode.objectify.impl.LoadEngine$1.nowUncached(LoadEngine.java:141)
    at com.googlecode.objectify.impl.LoadEngine$1.nowUncached(LoadEngine.java:127)
    at com.googlecode.objectify.util.ResultCache.now(ResultCache.java:30)
    at com.googlecode.objectify.impl.Round$1.nowUncached(Round.java:71)
    at com.googlecode.objectify.util.ResultCache.now(ResultCache.java:30)
    at com.googlecode.objectify.impl.LoaderImpl.now(LoaderImpl.java:251)
    at com.googlecode.objectify.impl.ref.LiveRef.get(LiveRef.java:47)
    at com.netkiller.erp.domain.Parent.getChild(Parent.java:187)
    at com.netkiller.common.dto.ApprovalTO.<init>(ApprovalTO.java:503)
    at com.netkiller.erp.service.ParentService$2.run(ParentService.java:1708)
    at com.netkiller.erp.service.ParentService$2.run(ParentService.java:1)
    at com.googlecode.objectify.ObjectifyService.run(ObjectifyService.java:81)
    at com.netkiller.erp.service.ParentService.lambda$8(ParentService.java:1706)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at com.google.apphosting.runtime.ApiProxyImpl$CurrentRequestThreadFactory.lambda$newThread$0(ApiProxyImpl.java:1213)
    at java.security.AccessController.doPrivileged(Native Method)
    at com.google.apphosting.runtime.ApiProxyImpl$CurrentRequestThreadFactory.lambda$newThread$1(ApiProxyImpl.java:1209)
    at java.lang.Thread.run(Thread.java:748)
    at com.google.apphosting.runtime.ApiProxyImpl$CurrentRequestThread.run(ApiProxyImpl.java:1185)
Caused by: java.util.NoSuchElementException
    at java.util.ArrayDeque.removeLast(ArrayDeque.java:295)
    at com.googlecode.objectify.impl.translate.LoadContext.exitContainerContext(LoadContext.java:141)
    at com.googlecode.objectify.impl.translate.ClassPopulator.load(ClassPopulator.java:119)
    at com.googlecode.objectify.impl.translate.ClassTranslator.loadSafe(ClassTranslator.java:122)
    at com.googlecode.objectify.impl.translate.ClassTranslator.loadSafe(ClassTranslator.java:21)
    at com.googlecode.objectify.impl.translate.NullSafeTranslator.load(NullSafeTranslator.java:17)
    at com.googlecode.objectify.impl.EntityMetadata.load(EntityMetadata.java:74)
    ... 22 more

感谢您对此问题的任何见识。

1 个答案:

答案 0 :(得分:1)

很难从提供的代码中确切地知道发生了什么,但是在多个线程之间进行操作时很容易意外污染数据。

为减少意外从错误线程的会话中提取数据的可能性,请使用ofy().load()加载所有内容,而不要遵循引用。您可以将Ref传递给ofy().load().ref(theRef)

如果您始终调用ofy(),那么您将始终获得正确的执行线程实例。尝试一下,如果仍然有问题,我们将继续讨论,并显示任何错误消息。