On Sat, Oct 30, 2010 at 12:56:53PM -0200, Rodrigo Rosenfeld Rosas wrote: > I'm finally back to Vitória, after a great time in RubyConfBR, even > though I've been target of jokes from Aaron Patterson regarding > Rspec questions due my questions to David Chelimsky in a non-related > talk from him. But that's ok, it was fun and I deserved it :)
I think I was poking fun at David, not you. ;-) > When listening to Yehuda's talk about development on the client > side, which was really great by the way, I got really worried when > he commented about some Rails validations not being concurrent-safe > nor even thread-safe. > > While I can understand it is hard to guarantee uniqueness validation > among different server instances, this can be easily avoided in a > single server configuration with config.threadsafe! enabled. > > Actually, I can't understand why thread-safe isn't enabled by > default in production, since we have much better thread support in > MRI 1.9.2 and always had on JRuby. > > I've just read the documentation for validates_uniqueness_of and it > explains well the problem: > > http://api.rubyonrails.org/classes/ActiveRecord/Validations/ClassMethods.html#method-i-validates_uniqueness_of > > But I was thinking if Rails could provide some way for avoiding > dealing with exceptions when using a single multi-thread server > environment. For instance: > > @record.validate :lock => @shared_lock do > # code run if @record.valid? > ... > @record.save > end or (render :edit; return) > redirect_to :list > ... > > The :lock parameter should be optional and an internal lock (one per > model, maybe) should be used when not specified in the case it is > not necessary to share this code with another block which could also > affect the record, for instance. A shared lock like this would work if you only had one process running. It won't work for people that run multiple processes as they won't share the lock. > An error should raise if 'config.threadsafe!' is not enabled and it > should be pointed out in the docs that this won't work for multiple > servers setup, for avoiding confusing end-developer users. Maybe a > warning instead of an error should suffice, for allowing this usage > by plugins that don't have control of the deployment decisions. > > If the user calls 'save' directly without validating first, the > validation and save operations shoud be atomic in this case. So, > 'save' should also support the :lock parameter. > > Is this reasonable or am I missing something? Even people running multi threaded servers run multiple processes. You could use the database connection to create a shared lock. Though, IMHO if you want to guarantee a unique column, you should add a unique index on the column. The uniqueness validation should work most of the time, and for edge cases, the user could see an exception. If you're really paranoid, you could implement it now with something like this (note this is mysql specific): def shared_lock(name) r = AR::Base.connection.execute("SELECT GET_LOCK('#{name}', 2)") # ... make sure to check the return value ... yield ensure AR::Base.connection.execute("SELECT RELEASE_LOCK('#{name}')") end def some_function # this function must calculate a value that you can reproduce across # servers and processes end shared_lock(some_function) do my_model.save! end Read here for more info: http://dev.mysql.com/doc/refman/4.1/en/miscellaneous-functions.html#function_get-lock There may be a way to do this in a non-database specific way (a lock server or something). -- Aaron Patterson http://tenderlovemaking.com/
pgpG8aT1wAkRC.pgp
Description: PGP signature
