会话哈希中有一个数组,我正在向其中添加内容。问题是,有时会同时处理多个请求(因为 ajax),然后请求对数组所做的更改会被第二个请求所做的更改所取代。
例如,数组首先看起来像这样:
[63, 73, 92]
然后第一个请求添加了一些东西:
[63、73、92、84]
第二个请求做同样的事情(但显然适用于旧版本):
[63、73、92、102]
所以最后数组看起来不像它应该的那样。有没有办法避免这种情况?我尝试使用缓存存储、活动记录存储和 cookie 存储。他们所有人都有同样的问题。
谢谢。
会话哈希中有一个数组,我正在向其中添加内容。问题是,有时会同时处理多个请求(因为 ajax),然后请求对数组所做的更改会被第二个请求所做的更改所取代。
例如,数组首先看起来像这样:
[63, 73, 92]
然后第一个请求添加了一些东西:
[63、73、92、84]
第二个请求做同样的事情(但显然适用于旧版本):
[63、73、92、102]
所以最后数组看起来不像它应该的那样。有没有办法避免这种情况?我尝试使用缓存存储、活动记录存储和 cookie 存储。他们所有人都有同样的问题。
谢谢。
在 Rails 中确实没有很好的解决方案。我的第一个建议是检查您的用例,看看您是否可以从一开始就避免这种情况。如果这样做是安全的,那么会话数据通常最好保存在客户端上,因为在处理服务器端会话存储时可能会遇到许多挑战。另一方面,如果这些数据可能在多个页面请求(可能还有多个会话)中长期有用,那么它可能应该进入数据库。
当然,有些数据确实属于会话(当前登录的用户就是一个很好的例子)。如果是这种情况,请查看http://paulbutcher.com/2007/05/01/race-conditions-in-rails-sessions-and-how-to-fix-them/,特别是https:// /github.com/fcheung/smart_session_store,它试图处理你描述的情况。
会话竞争条件在 Rails 中很常见。Redis 会话存储也无济于事!原因是 Rails 仅在收到请求时读取并创建会话对象,并在请求完成并即将返回给用户时将其写回会话存储。
Java 有一个解决方案,称为会话复制。我们可以构建自己的基于 redis 的并发会话存储。下面就差不多了。除了更新方法缺少锁。但它几乎从来没有发生过比赛条件。
要获取会话哈希,只需使用返回哈希的 concurrent_session 。要在其中设置一些值,请使用 update_concurrent_session 方法。它将新哈希深度合并到旧值中:
class ApplicationController < ActionController::Base
def concurrent_session
@concurrent_session ||= get_concurrent_session
end
def update_concurrent_session h
return unless session.id.present?
@concurrent_session = get_concurrent_session.deep_merge(h)
redis.set session_key, @concurrent_session.to_json
redis.expire session_key, session_timeout
end
private
def redis
Rails.cache.redis
end
def session_timeout
2.weeks.to_i
end
def session_key
'SESSION_' + session.id.to_s if session.id.present?
end
def get_concurrent_session
return {} unless session.id.present?
redis.expire session_key, session_timeout
redis.get(session_key).yield_self do |v|
if v
JSON.parse v, symbolize_names: true
else
{}
end
end
end
end
使用示例:
my_roles = concurrent_session[:my_roles]
update_concurrent_session({my_roles: ['admin', 'vip']})
- 加载当前会话,或在必要时创建一个新会话
- 保存未修改会话的副本以供将来参考
- 运行动作代码
- 将修改后的会话与之前保存的副本进行比较,以确定发生了什么变化
- 如果会话已更改:
- 锁定会话
- 重新加载会话
- 应用对此会话所做的更改并保存
- 解锁会话
这是一个简单的竞争条件,只需使用任何锁定机制(如 redis locker )锁定请求
RedisLocker.new('my_ajax').run! { session[:whatever] << number }