Fixing the ConcurrentModificationException on init #1210
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This PR fixes issue #1118
Context
Most of the modern microservices are running behind the orchestrator or load balancer, for example in the Kubernetes cluster. The orchestrator is designed to perform regular checks of the service using the health check API. The response of the health check API is the only way for the orchestrator to know that the is up and running. As soon as the orchestrator starts the service process, it runs an infite loop of the health check requests.
Problem
Spark starts listening to the network as soon as the first route is registered. The problem is that the first request can come to the service before all routes are registered: after the first route is registered, but before the last one is registered, at the same time with one of the routes being registered. In this case, Spark will iterate over the routes list (looking for the route to handle the request) at the same time as the list could be modified by initialization. It would cause the following exception and the 500 error code returned to the client as a result:
Reproduction steps:
Please see the test case included in the PR
Solution:
Change the implementation of the
routes
list inside theRoutes
class fromArrayList
toCopyOnWriteArrayList
. Internally, CopyOnWriteArrayList would be copied every time the new element is added, so there is no way it could haveConcurrentModificationException
when iterating over it. It does not have any lock inside and hence iteration over it does not have any overhead compared toArrayList
.ConcurrentModificationException
has an extra overhead when adding the routes, but since route, registration is one-time action during the service start and most of the microservices should have a relatively low amount of routes, it should not be a concern.