[Dbix-class] Race condition in find_or_create()

Oleg Pronin syber.rus at gmail.com
Thu Jul 31 22:31:37 BST 2008


Not quite right.
There are two general case:
1) when you except most of queries to not find the row. Then your example is
quite ok.
    eval {create};
    if error then find;
    Most of cases there will be only 1 query.
2) when you expect to find (for example - updating daily stats once per
minute - only at 0:00 you will not find the row).
    In this case it must be:

    if find then return found;
    eval {create};
    if error then find again (it will succeed).

    Most of cases there will be only 1 query.

Maybe $rs->find_or_create({cond}, {first_try_create =3D> 1}) ? :)

2008/7/30 Ash Berlin <ash_cpan at firemirror.com>

>
> On 30 Jul 2008, at 15:44, Tobias Kremer wrote:
>
>  As many of you probably know, the current find_or_create() implementation
>> contains a race-condition. Catalyst::Plugin::Session::Store::DBIC is
>> especially prone to die in front of the user when using the flash() meth=
od -
>> even more so when AJAX calls are involved resulting in two or more
>> concurrent requests trying to select/insert the flash data into the data=
base
>> via find_or_create().
>>
>> I proposed the following reverse order implementation on the Catalyst
>> mailing list which will hopefully not only reduce the probability of a
>> race-condition but will also reduce find_or_create() to a single query in
>> the best case.
>>
>> On Tue, Jul 29, 2008 at 12:49:17PM +0200, Tobias Kremer wrote:
>>
>>> Maybe it'd be better to ditch find_or_create in favor of something like
>>> this:
>>> eval {
>>> $row =3D $self->model->create( { ... } );
>>> }
>>> if( $@ && $@ =3D~ /duplicate/ ) { # the question about detecting this
>>> remains
>>> $row =3D $self->model->find( { ... } );
>>> }
>>> This would reduce the query to 1 at best and 2 at worst.
>>>
>>
>>
>> mst responded with:
>>
>> On 30.07.2008, at 12:26, Matt S Trout wrote:
>>
>>> It's still probably viable.
>>> What I'd really like is to add some code to DBIC's storage to know what
>>> re
>>> that /duplicate/ is on whatever DB so we can bury this away from the us=
er
>>> and DBIC can Just Use It for find_or_create on any supported database.
>>> Maybe you could wander over to the DBIC list and poke at patching that
>>> instead?
>>>
>>
>> So, the problem is: How to detect if the insert failed due to a unique k=
ey
>> constraint for all supported databases?
>>
>> I'd volunteer to help implementing this but as of now I'm not that deep
>> into DBIC to know exactly how to pull this off. So I'd really appreciate=
 a
>> few ideas and thoughts.
>>
>> I suppose we could put a method returning a database-dependent regular
>> expression that detects unique key constraint errors into the
>> DBIx::Class::Storage::DBI::* classes and use that from within
>> find_or_create().
>>
>> What do you think?
>>
>> --Tobias
>>
>>
> Yes but not quite like that.
>
> A method on the storage object (which is subclassed to be the particular
> type of DBI you are connecting to, eg DBIx::Class::Storage::DBI::mysql)
> which parses and encapsulates the error as returned from the database to
> mark its type, e.g. FK constraint violation, duplicate key, etc. etc.
>
> Then you could check $@->type eq 'duplicate_key' or something.
>
> Comments?
>
>
>
> _______________________________________________
> List: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/dbix-class
> IRC: irc.perl.org#dbix-class
> SVN: http://dev.catalyst.perl.org/repos/bast/DBIx-Class/
> Searchable Archive:
> http://www.grokbase.com/group/dbix-class@lists.rawmode.org
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.scsys.co.uk/pipermail/dbix-class/attachments/20080801/5e4=
0d253/attachment.htm


More information about the DBIx-Class mailing list