首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Mongoid4 (GitHub主机)创建具有重复ID的文档

Mongoid4 (GitHub主机)创建具有重复ID的文档
EN

Stack Overflow用户
提问于 2014-01-11 05:42:39
回答 6查看 749关注 0票数 3

我正在使用Sidekiq进行高流量测试,它使用Mongoid作为我在Rails4应用程序中的驱动程序来创建基于MongoDB的对象。我看到的问题是,当一个PlayByPlay文档应该有一个惟一的game_id时,我看到多个PlayByPlay对象被创建成具有完全相同的game_id。我也对MongoDB实施了独特的约束,而且这种情况仍在发生。这是我的文档,它是嵌入式文档,并简要介绍了我是如何创建文档的。问题是,这一切都是在使用Sidekiq的线程环境中发生的,我不确定是否有办法解决它。我的写入关注点在mongoid.yml中设置为1,看起来像是在master中删除了safe选项,persist_in_safe_mode也是如此。下面的代码--任何关于如何正确工作的建议都将不胜感激。这不是一个副本集,它是执行所有读/写请求的单个MongoDB服务器。

代码语言:javascript
复制
module MLB
    class Play
        include Mongoid::Document
        include Mongoid::Timestamps

        embedded_in :play_by_play

        field :batter#, type: Hash
        field :next_batter#, type: Hash
        field :pitchers#, type: Array
        field :pitches#, type: Array
        field :fielders#, type: Array
        field :narrative, type: String
        field :seq_id, type: Integer
        field :inning, type: Integer
        field :outs
        field :no_play
        field :home_team_score
        field :away_team_score
    end
    class PlayByPlay 
        include Mongoid::Document
        include Mongoid::Timestamps

        embeds_many :plays, cascade_callbacks: true
        accepts_nested_attributes_for :plays

        field   :sport
        field :datetime, type: DateTime
        field :gamedate, type: DateTime
        field :game_id
        field :home_team_id
        field :away_team_id
        field :home_team_score
        field :away_team_score
        field :season_year
        field :season_type
        field :location
        field :status
        field :home_team_abbr
        field :away_team_abbr
        field :hp_umpire
        field :fb_umpire
        field :sb_umpire
        field :tb_umpire

        index({game_id: 1})
        index({away_team_id: 1})
        index({home_team_id: 1})
        index({season_type: 1})
        index({season_year: 1})

        index({"plays.seq_id" => 1}, {unique: true, drop_dups: true})
        #validates 'play.seq_id', uniqueness: true
        validates :game_id, presence: true, uniqueness: true
        validates :home_team_id, presence: true
        validates :away_team_id, presence: true
        validates :gamedate, presence: true
        validates :datetime, presence: true
        validates :season_type, presence: true
        validates :season_year, presence: true

        def self.parse!(entry)
            @document = Nokogiri::XML(entry.data)
            xslt = Nokogiri::XSLT(File.read("#{$XSLT_PATH}/mlb_pbp.xslt"))
            transform = xslt.apply_to(@document)
            json_document = JSON.parse(transform)

            obj = find_or_create_by(game_id: json_document['game_id'])
            obj.sport                   = json_document['sport']
            obj.home_team_id        = json_document['home_team_id']
            obj.away_team_id        = json_document['away_team_id']
            obj.home_team_score = json_document['home_team_score']
            obj.away_team_score = json_document['away_team_score']
            obj.season_type         = json_document['season_type']
            obj.season_year         = json_document['season_year']
            obj.location                = json_document['location']
          obj.datetime              =   DateTime.strptime(json_document['datetime'], "%m/%d/%y %H:%M:%S")
            obj.gamedate                = DateTime.strptime(json_document['game_date'], "%m/%d/%Y %H:%M:%S %p")
            obj.status                  = json_document['status']
            obj.home_team_abbr  = json_document['home_team_abbr']
            obj.away_team_abbr  = json_document['away_team_abbr']
            obj.hp_umpire           = json_document['hp_umpire']
            obj.fb_umpire           = json_document['fb_umpire']
            obj.sb_umpire           = json_document['sb_umpire']
            obj.tb_umpire           = json_document['tb_umpire']
            p=obj.plays.build(seq_id: json_document['seq_id'])
            p.batter            =   json_document['batter']
            p.next_batter = json_document['next_batter'] if json_document['next_batter'].present? && json_document['next_batter'].keys.count >= 1
            p.pitchers      = json_document['pitchers'] if json_document['pitchers'].present? && json_document['pitchers'].count >= 1
            p.pitches       =   json_document['pitches'] if json_document['pitches'].present? && json_document['pitches'].count >= 1
            p.fielders      = json_document['fielders'] if json_document['fielders'].present? && json_document['fielders'].count >= 1
            p.narrative     =   json_document['narrative']
            p.seq_id            = json_document['seq_id']
            p.inning            = json_document['inning']
            p.outs              = json_document['outs']
            p.no_play       =   json_document['no_play']
            p.home_team_score = json_document['home_team_score']
            p.away_team_score = json_document['away_team_score']

            obj.save
        end

    end
end

**注意事项**

如果我将sidekiq限制为1个工人,这个问题就会消失,这在现实世界中显然是我永远不会做的。

EN

回答 6

Stack Overflow用户

发布于 2014-01-18 05:17:34

你已经在game_id上建立了索引,为什么不让它成为唯一的呢?这样,即使mongoid未能正确地进行验证(@vidaica的答案描述了mongoid如何无法验证唯一性),db也将不允许重复条目。

尝试添加唯一索引

index({"game_id" => 1}, {unique: true})

然后

rake db:mongoid:create_indexes

在mongo中创建它们(请确保它是从mongo shell创建的)。

在那之后,mongodb不应该持久化任何带有重复game_id的记录,你在ruby层要做的就是处理你将从mongodb收到的插入错误。

票数 3
EN

Stack Overflow用户

发布于 2014-01-13 10:52:31

这是因为许多线程插入具有相同game_id的对象。让我来解释一下。

例如,您有两个SideKiq线程t1和t2。它们是并行运行的。假设您有一个包含game_id 1的文档,并且它还没有插入到数据库中。

  1. t1进入parse方法,它在使用game_id 1的数据库中看不到任何文档,它使用game_id 1创建文档并继续填充其他数据,但它尚未保存该文档。
  2. t2进入parse方法,它在使用game_id 1的数据库中看不到任何文档,因为此时t1尚未保存该文档。t2创建具有相同game_id 1.
  3. t1的文档保存文档
  4. t2保存文档

结果是:您有两个具有相同game_id 1的文档。

为了防止这种情况,您可以使用Mutex来序列化解析代码的访问。要了解如何使用互斥锁,请阅读以下内容:http://www.ruby-doc.org/core-2.0.0/Mutex.html

票数 2
EN

Stack Overflow用户

发布于 2014-01-20 02:44:51

无论你做什么,你都会想要在数据库级别上解决这个问题,因为你几乎肯定会在实现独特约束方面做得比mongo人做的更糟糕。

假设有一天你想要分片,或者考虑mongo的水平可伸缩性特性(你正在做大容量测试,所以我假设这是你不想在设计上排除的),可能没有可靠的方法来做到这一点(请参阅Ramifications of working with a mongodb clustersharding concepts):

假设我们正在对电子邮件进行分片,并希望在用户名上有一个唯一的索引。这在集群中是不可能强制执行的。

但是,如果您在game_id上进行分片,或者根本不考虑分片,那么在game_id上设置唯一索引应该可以防止重复记录(参见@xlembouras answer)。

但是,当由于竞争条件而违反此索引时,答案可能无法阻止异常,因此请确保抢救异常并执行更新,而不是在抢救块中创建(可能通过使用@new_record (click 'Show source'),将尝试找出时间给您确切代码)。

更新,简短快速回答

代码语言:javascript
复制
begin
  a = Album.new(name: 'foo', game_id: 3)
  a.save
rescue
  a.id = id_of_the_object_with_same_id_already_in_db
  a.instance_variable_set('@new_record', false)
  a.save
end
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/21054934

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档