On Thursday, January 29, 2015 at 11:38:35 AM UTC-8, Nels Nelson wrote:
>
> I have a relationship on a model class defined in this way:
>
> create_table :tag do
>   primary_key :id
>   index :name
>   String :name, :unique => true, :null => false
> end
>
> class Tag < Sequel::Model
> end
>
> create_table :tagged do
>   primary_key :id
>   foreign_key :thing_id, :thing, :on_delete => :cascade
>   foreign_key :tag_id, :tag, :on_delete => :cascade
> end
>
> class Tagged < Sequel::Model
> end
>
> create_table :thing do
>   primary_key :id
>   foreign_key :parent_id, : thing, :on_delete => :set_null
>   index :name
>   String :name, :text => true
>   String :thing_type, :null => false
> end
>
> class Thing < Sequel::Model
>   plugin :rcte_tree
>   plugin :single_table_inheritance, :thing_type
>   one_to_many :tagged, :key => :thing_id
>   many_to_many :tags, :class => 'Tag', :join_table => :tagged, :left_key 
> => :thing_id, :right_key => :tag_id
> end
>
>
> My environment looks like this:
>
> $ java -version
> java version "1.6.0_65"
> Java(TM) SE Runtime Environment (build 1.6.0_65-b14-462)
> Java HotSpot(TM) 64-Bit Server VM (build 20.65-b04-462, mixed mode)
> $ uname -a
> Darwin sol.local 14.0.0 Darwin Kernel Version 14.0.0: Fri Sep 19 00:26:44 
> PDT 2014; root:xnu-2782.1.97~2/RELEASE_X86_64 x86_64
> $ jruby --version
> jruby 1.7.16 (1.9.3p392) 2014-09-25 575b395 on Java HotSpot(TM) 64-Bit 
> Server VM 1.6.0_65-b14-462 +jit [darwin-x86_64]
> $ jgem list sequel
>
>
> *** LOCAL GEMS ***
>
>
> sequel (4.15.0)
>
>
> When running in a multi-threaded environment, occasionally, this happens:
>
> o = Thing.new(name: 'test')
> o.tags << Tag.new(name: 'alpha')
> o.tags << Tag.new(name: 'beta')
>
> 10000.times do
>   # This is dumb, and probably wouldn't actually work, it's just an 
> illustration
>   Thread.new do
>     thing = Thing.find(id: 1)
>
>     # Almost always:
>     some_tags = thing.tags #=> [ #<Tag:0x1c:14>, #<Tag:0x1d:15> ]
>
>     # But every once in a while:
>     some_tags = thing.tags #=> nil
>   end
> end
>

This sounds like a race condition.  Unfortunately, I'm unable to replicate. 
 Here's the code I used:

DB.create_table :tags do
  primary_key :id
  index :name
  String :name, :unique => true, :null => false
end

DB.create_table :things do
  primary_key :id
  foreign_key :parent_id, :things, :on_delete => :set_null
  index :name
  String :name, :text => true
  String :thing_type, :null => false
end

DB.create_table :taggings do
  primary_key :id
  foreign_key :thing_id, :things, :on_delete => :cascade
  foreign_key :tag_id, :tags, :on_delete => :cascade
end

class Tag < Sequel::Model
end

class Tagging < Sequel::Model
end

class Thing < Sequel::Model
  plugin :rcte_tree
  plugin :single_table_inheritance, :thing_type
  one_to_many :tagged, :key => :thing_id
  many_to_many :tags, :class => 'Tag', :join_table => :taggings, :left_key 
=> :thing_id, :right_key => :tag_id
end

o = Thing.create(name: 'test')
o.add_tag(Tag.create(name: 'alpha'))
o.add_tag(Tag.create(name: 'beta'))

100.times do |i|
  puts "starting pass #{i+1}"
  threads = []
  1000.times do
    threads << Thread.new do
      raise unless Thing.find(id: 1).tags
    end
  end
  threads.each{|t| t.join}
end

This doesn't create 10,000 threads at the same time, as I ran into pool 
timeouts with the default settings when trying that in my environment, 
which is:

jruby 1.7.6 (1.9.3p392) 2013-10-22 6004147 on Java HotSpot(TM) 64-Bit 
Server VM 1.7.0_07-b10 [Windows 7-amd64]

With max_connections at 50 and pool_timeout at 120, I was able to get 10000 
threads working, and still was not able to reproduce, using the following 
loop:

10.times do |i|
  puts "starting pass #{i+1}"
  threads = []
  10000.times do
    threads << Thread.new do
      raise unless Thing.find(id: 1).tags
    end
  end
  threads.each{|t| t.join}
end

That's not to say there isn't a race condition, but it must be very hard to 
hit.  Could you try adding the :instance_specific => true option when 
defining the tags association and see if the problem still occurs?  If the 
problem goes away, I can better focus the search for the underlying issue.

Note that I could easily see this issue happening if you have multiple 
threads operating on the same model instance.  That's not what your example 
code shows, though, since it is returning a new instance inside each 
thread.  But if you have multiple threads operating on a single model 
instance, all bets are off, as mentioned 
in 
http://sequel.jeremyevans.net/rdoc/files/doc/thread_safety_rdoc.html#label-Exceptions.

Thanks,
Jeremy

-- 
You received this message because you are subscribed to the Google Groups 
"sequel-talk" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/sequel-talk.
For more options, visit https://groups.google.com/d/optout.

Reply via email to