opentripplanner / OpenTripPlanner

An open source multi-modal trip planner
http://www.opentripplanner.org
Other
2.12k stars 1.01k forks source link

Concurrent modification exception in Timetable #5933

Open vpaturet opened 1 week ago

vpaturet commented 1 week ago

We see occasionally in our production environment a concurrent access issue on a Timetable object. It seems that a reader thread (GraphQL API call) iterates over the TripTimes collection of a Timetable object while this collection is being modified concurrently by another thread. In theory this should not happen since the TimetableSnapshot should ensure that all write operations on Timetable objects happen before the Timetables are handed over to reader threads.

Exception while fetching data (/stopPlaces[0]/estimatedCalls) : null
java.util.ConcurrentModificationException: null
    at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1095)
    at java.base/java.util.ArrayList$Itr.next(ArrayList.java:1049)
    at org.opentripplanner.routing.stoptimes.StopTimesHelper.listTripTimeShortsForPatternAtStop(StopTimesHelper.java:247)
    at org.opentripplanner.routing.stoptimes.StopTimesHelper.stopTimesForStop(StopTimesHelper.java:65)
    at org.opentripplanner.transit.service.DefaultTransitService.stopTimesForStop(DefaultTransitService.java:325)
    at org.opentripplanner.apis.transmodel.model.stop.StopPlaceType.getTripTimesForStop(StopPlaceType.java:438)
    at org.opentripplanner.apis.transmodel.model.stop.StopPlaceType.lambda$create$16(StopPlaceType.java:378)
    at java.base/java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.java:273)
    at java.base/java.util.HashMap$KeySpliterator.tryAdvance(HashMap.java:1736)
    at java.base/java.util.stream.ReferencePipeline.forEachWithCancel(ReferencePipeline.java:129)
    at java.base/java.util.stream.AbstractPipeline.copyIntoWithCancel(AbstractPipeline.java:527)
    at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:513)
    at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
    at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:921)
    at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
    at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:682)
    at org.opentripplanner.apis.transmodel.model.stop.StopPlaceType.lambda$create$17(StopPlaceType.java:400)
    at graphql.execution.ExecutionStrategy.invokeDataFetcher(ExecutionStrategy.java:329)
    at graphql.execution.ExecutionStrategy.fetchField(ExecutionStrategy.java:305)
s   at graphql.execution.ExecutionStrategy.fetchField(ExecutionStrategy.java:243)
    at graphql.execution.ExecutionStrategy.resolveFieldWithInfo(ExecutionStrategy.java:214)
    at graphql.execution.AsyncExecutionStrategy.execute(AsyncExecutionStrategy.java:63)
    at graphql.execution.ExecutionStrategy.completeValueForObject(ExecutionStrategy.java:729)
    at graphql.execution.ExecutionStrategy.completeValue(ExecutionStrategy.java:502)
    at graphql.execution.ExecutionStrategy.completeValueForList(ExecutionStrategy.java:613)
    at graphql.execution.ExecutionStrategy.completeValueForList(ExecutionStrategy.java:561)
    at graphql.execution.ExecutionStrategy.completeValue(ExecutionStrategy.java:487)
    at graphql.execution.ExecutionStrategy.completeField(ExecutionStrategy.java:453)
    at graphql.execution.ExecutionStrategy.lambda$resolveFieldWithInfo$1(ExecutionStrategy.java:216)
    at java.base/java.util.concurrent.CompletableFuture.uniApplyNow(CompletableFuture.java:684)
    at java.base/java.util.concurrent.CompletableFuture.uniApplyStage(CompletableFuture.java:662)
    at java.base/java.util.concurrent.CompletableFuture.thenApply(CompletableFuture.java:2200)
    at graphql.execution.ExecutionStrategy.resolveFieldWithInfo(ExecutionStrategy.java:215)
    at graphql.execution.AsyncExecutionStrategy.execute(AsyncExecutionStrategy.java:63)
    at graphql.execution.Execution.executeOperation(Execution.java:162)
    at graphql.execution.Execution.execute(Execution.java:104)
    at graphql.GraphQL.execute(GraphQL.java:568)
    at graphql.GraphQL.lambda$parseValidateAndExecute$13(GraphQL.java:487)
    at java.base/java.util.concurrent.CompletableFuture.uniComposeStage(CompletableFuture.java:1187)
    at java.base/java.util.concurrent.CompletableFuture.thenCompose(CompletableFuture.java:2341)
    at graphql.GraphQL.parseValidateAndExecute(GraphQL.java:482)
    at graphql.GraphQL.lambda$executeAsync$9(GraphQL.java:440)
    at java.base/java.util.concurrent.CompletableFuture.uniComposeStage(CompletableFuture.java:1187)
    at java.base/java.util.concurrent.CompletableFuture.thenCompose(CompletableFuture.java:2341)
    at graphql.GraphQL.executeAsync(GraphQL.java:428)
    at graphql.GraphQL.execute(GraphQL.java:366)
    at org.opentripplanner.apis.transmodel.TransmodelGraph.executeGraphQL(TransmodelGraph.java:66)
    at org.opentripplanner.apis.transmodel.TransmodelAPI.getGraphQL(TransmodelAPI.java:113)
    at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
    at java.base/java.lang.reflect.Method.invoke(Method.java:580)
    at org.glassfish.jersey.server.model.internal.ResourceMethodInvocationHandlerFactory.lambda$static$0(ResourceMethodInvocationHandlerFactory.java:52)
    at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher$1.run(AbstractJavaResourceMethodDispatcher.java:146)
    at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.invoke(AbstractJavaResourceMethodDispatcher.java:189)
    at org.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$ResponseOutInvoker.doDispatch(JavaResourceMethodDispatcherProvider.java:176)
    at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.dispatch(AbstractJavaResourceMethodDispatcher.java:93)
    at org.glassfish.jersey.server.model.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:478)
    at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:400)
    at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:81)
    at org.glassfish.jersey.server.ServerRuntime$1.run(ServerRuntime.java:274)
    at org.glassfish.jersey.internal.Errors$1.call(Errors.java:248)
    at org.glassfish.jersey.internal.Errors$1.call(Errors.java:244)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:292)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:274)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:244)
    at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:266)
    at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:253)
    at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:696)
    at org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpContainer.service(GrizzlyHttpContainer.java:367)
    at org.glassfish.grizzly.http.server.HttpHandler$1.run(HttpHandler.java:190)
    at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:535)
    at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:515)
    at java.base/java.lang.Thread.run(Thread.java:1583)