3

会话哈希中有一个数组,我正在向其中添加内容。问题是,有时会同时处理多个请求(因为 ajax),然后请求对数组所做的更改会被第二个请求所做的更改所取代。

例如,数组首先看起来像这样:

[63, 73, 92]

然后第一个请求添加了一些东西:

[63、73、92、84]

第二个请求做同样的事情(但显然适用于旧版本):

[63、73、92、102]

所以最后数组看起来不像它应该的那样。有没有办法避免这种情况?我尝试使用缓存存储、活动记录存储和 cookie 存储。他们所有人都有同样的问题。

谢谢。

4

4 回答 4

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,它试图处理你描述的情况。

于 2013-05-15T00:52:29.203 回答
1

会话竞争条件在 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']})
于 2020-08-12T11:26:46.750 回答
0
  1. 加载当前会话,或在必要时创建一个新会话
  2. 保存未修改会话的副本以供将来参考
  3. 运行动作代码
  4. 将修改后的会话与之前保存的副本进行比较,以确定发生了什么变化
  5. 如果会话已更改:
    1. 锁定会话
    2. 重新加载会话
    3. 应用对此会话所做的更改并保存
    4. 解锁会话

来自Rails 会话中的竞争条件以及如何修复它们

于 2013-05-15T01:06:16.397 回答
0

这是一个简单的竞争条件,只需使用任何锁定机制(如 redis locker )锁定请求

RedisLocker.new('my_ajax').run! { session[:whatever] << number }
于 2013-08-15T03:06:45.753 回答