我正在使用Sidekiq进行高流量测试,它使用Mongoid作为我在Rails4应用程序中的驱动程序来创建基于MongoDB的对象。我看到的问题是,当一个PlayByPlay文档应该有一个惟一的game_id时,我看到多个PlayByPlay对象被创建成具有完全相同的game_id。我也对MongoDB实施了独特的约束,而且这种情况仍在发生。这是我的文档,它是嵌入式文档,并简要介绍了我是如何创建文档的。问题是,这一切都是在使用Sidekiq的线程环境中发生的,我不确定是否有办法解决它。我的写入关注点在mongoid.yml中设置为1,看起来像是在master中删除了safe选项,persist_in_safe_mode也是如此。下面的代码--任何关于如何正确工作的建议都将不胜感激。这不是一个副本集,它是执行所有读/写请求的单个MongoDB服务器。
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个工人,这个问题就会消失,这在现实世界中显然是我永远不会做的。
发布于 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收到的插入错误。
发布于 2014-01-13 10:52:31
这是因为许多线程插入具有相同game_id的对象。让我来解释一下。
例如,您有两个SideKiq线程t1和t2。它们是并行运行的。假设您有一个包含game_id 1的文档,并且它还没有插入到数据库中。
parse方法,它在使用game_id 1的数据库中看不到任何文档,它使用game_id 1创建文档并继续填充其他数据,但它尚未保存该文档。parse方法,它在使用game_id 1的数据库中看不到任何文档,因为此时t1尚未保存该文档。t2创建具有相同game_id 1.结果是:您有两个具有相同game_id 1的文档。
为了防止这种情况,您可以使用Mutex来序列化解析代码的访问。要了解如何使用互斥锁,请阅读以下内容:http://www.ruby-doc.org/core-2.0.0/Mutex.html
发布于 2014-01-20 02:44:51
无论你做什么,你都会想要在数据库级别上解决这个问题,因为你几乎肯定会在实现独特约束方面做得比mongo人做的更糟糕。
假设有一天你想要分片,或者考虑mongo的水平可伸缩性特性(你正在做大容量测试,所以我假设这是你不想在设计上排除的),可能没有可靠的方法来做到这一点(请参阅Ramifications of working with a mongodb cluster和sharding concepts):
假设我们正在对电子邮件进行分片,并希望在用户名上有一个唯一的索引。这在集群中是不可能强制执行的。
但是,如果您在game_id上进行分片,或者根本不考虑分片,那么在game_id上设置唯一索引应该可以防止重复记录(参见@xlembouras answer)。
但是,当由于竞争条件而违反此索引时,答案可能无法阻止异常,因此请确保抢救异常并执行更新,而不是在抢救块中创建(可能通过使用@new_record (click 'Show source'),将尝试找出时间给您确切代码)。
更新,简短快速回答
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
endhttps://stackoverflow.com/questions/21054934
复制相似问题