英文:
Spring Session with MongoDB: race condition?
问题
我们有一个位于负载均衡器后面的应用程序,仅支持轮询,不支持粘性会话。
Spring Boot应用程序在OpenShift集群中运行,部署中有多个Pod。我们至少有两个Pod,但根据负载的情况,可能会扩展到10或20个。
该应用程序使用Spring Session和Spring Security。Spring Session配置为使用MongoDB(而不是Redis),因为应用程序已经在其他方面使用MongoDB。
在低到中等负载的功能测试中,我们注意到会话属性会偶尔“丢失”:更新这些条目的代码成功运行,但在请求完成后,会话中仍然保留旧的属性内容。这种情况是随机发生的。
在应用程序的单个实例上进行测试时,并没有观察到这样的情况。
对我来说,这似乎是一个在将会话对象写回Mongo时的竞争条件,其中一个Pod上的某个HTTP请求与另一个Pod上的写回竞速,导致“错误的一个”获胜。
- 这是Spring Session与MongoDB的有效使用场景吗?换句话说,这应该正常工作吗?
- 如果应该正常工作,我该如何找出发生了什么,并如何解决这个问题?
- 如果不应该正常工作,是否有一种Spring Session设置可以允许在没有竞争条件的情况下共享跨应用程序服务器的会话状态?
英文:
We have an application behind a load balancer that only supports round-robin, no sticky sessions.
The Spring Boot application runs in an OpenShift cluster, with a number of pods in a deployment. We have at least two pods, but might scale this up to 10 or 20, depending on load.
The application uses Spring Session and Spring Security. Spring Session is configured to use MongoDB (not Redis), since the application is already using MongoDB for other purposes.
During functional testing with low to moderate load, we have noticed issues with session attributes "going missing": the code that updates these entries runs successfully, but after the request is finished, the older contents of the attributes is in the session. This happens randomly.
Testing with a single instance of the application, no such observations were made.
To me, this smells like a race condition between the write back of the session object to Mongo, with some HTTP request on one pod racing the write back in another pod, and the "wrong one" winning.
- Is this a valid usage scenario for Spring Session with MongoDB? In other words, is this supposed to work?
- If it is supposed to work, how can I find out what's happening, and what can I do to solve the issue?
- If it's not supposed to work, is there a Spring Session setup that would allow a cross application server sharing of session state without race conditions?
答案1
得分: 2
我们花了很多时间与数据库团队一起尝试弄清楚这是否与MongoDB客户端连接或服务器配置有关,但经过更深入的研究,我找到了问题的根源:它是由于spring-session-data-mongodb,因为它未能实现增量更新。
https://github.com/spring-projects/spring-session-data-mongodb/issues/106
问题在于没有逻辑来检查是否需要将会话存储库写入,或者跟踪会话的哪些属性已更改。会话在每个请求结束时都会无条件地写回。
如果您有多个并发请求,就像任何普通的Web应用程序一样,会话状态将从最后开始并最先结束的请求中继承下来。因此,一个简单的图像检索(通过Spring处理程序处理)将导致会话被写回。如果您像我们一样有一个登录处理程序,需要大量时间(最多2秒)因为它从后端系统检索了大量用户信息,那么图像请求将在登录请求之后启动,但在其之前完成。然后,spring-session-data-mongodb决定登录处理程序的会话状态已过时,并且无法保存它。
因此,在这个错误修复之前,我们将需要使用不同的存储库,比如Redis。
英文:
We spent a lot of time with the DB team trying to figure out if this had anything to do with the MongoDB client connection or server configuration, but after some more thorough research, I've found the culprit: it's spring-session-data-mongodb, because it fails to implement delta updates.
https://github.com/spring-projects/spring-session-data-mongodb/issues/106
The problem is that there is no logic to check if a write to the session repository is necessary, or any tracking which attributes of the session have been changed. The session is written back at the end of every request unconditionally.
If you have multiple concurrent requests, like any normal web application has, the session state survives from the request that was started last and finished first. So a simple image retrieval (that is handled through a Spring handler) will cause the session to be written back. If you have, like we do, a login handler that takes a substantial amount of time (up to 2 seconds) because it retrieves a bunch of user info from a backend system, the request for the image will have started after the login request, but will have finished before it. spring-session-data-mongodb then decides that the session state from the login handler is stale, and fails to save it.
So until that bug is fixed, we will need to use a different repository like Redis.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论