会话哈希中有一个数组,我正在向它添加内容。问题是,有时会同时处理多个请求(因为ajax),然后请求对数组所做的更改将替换为第二个请求所做的更改。
示例,数组首先如下所示:
[63,73,92]
然后第一个请求添加了一些内容:
[63,73,92,84]
第二个请求做同样的事情(但显然适用于旧版本):
[63,73,92,102]
所以最后数组看起来不应该。有没有办法避免这种情况? 我试图使用缓存存储,活动记录存储和cookie存储。所有这些都存在同样的问题。
感谢。
答案 0 :(得分:1)
在Rails中确实没有一个很好的解决方案。我的第一个建议是检查你的用例,看看你是否可以避免这种情况开始。如果这样做是安全的,会话数据通常最好保留在客户端上,因为在处理服务器端会话存储时可能会遇到许多挑战。另一方面,如果这是跨多个页面请求(可能是多个会话)长期有用的数据,也许它应该放在数据库中。
当然,有一些数据确实属于会话(一个很好的例子就是当前登录的用户)。如果是这种情况,请查看http://paulbutcher.com/2007/05/01/race-conditions-in-rails-sessions-and-how-to-fix-them/,特别是https://github.com/fcheung/smart_session_store,它会尝试处理您所描述的情况。
答案 1 :(得分:1)
会话竞争条件在Rails中非常常见。 Redis会话存储也无济于事! 原因是Rails仅在接收到请求时读取并创建会话对象,并在请求完成并即将返回给用户时将其写回到会话存储。
Java为此提供了一种称为会话复制的解决方案。我们可以建立自己的基于并发Redis的会话存储。以下是差不多的内容。除了更新方法缺少锁。但这几乎从来不会碰到种族问题。
要获取会话哈希,只需使用并发会话即可返回哈希。 要在其中设置一些值,请使用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']})
答案 2 :(得分:0)
- 加载当前会话,或在必要时创建新会话
- 保存未修改会话的副本以供将来参考
- 运行操作代码
- 将修改后的会话与之前保存的副本进行比较,以确定更改内容
- 如果会话已更改:
醇>
- 锁定会话
- 重新加载会话
- 应用对此会话所做的更改并保存
- 解锁会话
答案 3 :(得分:0)
这是一个简单的竞争条件,只需使用任何锁定机制锁定请求 redis locker
RedisLocker.new('my_ajax').run! { session[:whatever] << number }