develooper Front page | perl.dbi.users | Postings from August 2006

peer review: recipe for solving SELECT/INSERT/UPDATE race condition

Thread Next
From:
Mark Stosberg
Date:
August 21, 2006 17:50
Subject:
peer review: recipe for solving SELECT/INSERT/UPDATE race condition
Message ID:
ecdkbm$ck6$1@sea.gmane.org

Sometimes it's desirable to first to a SELECT to see if a row exists,
and then INSERT if it doesn't already, or UPDATE if it does.

Using this pattern on a busy website, I noticed a race condition:
two SELECTs could happen at nearly the same time, and both decide to
INSERT. The second INSERT fails, unless a check is more for it.

Here's the recipe I'm trying to out to address the race condition now,
targeting PostgreSQL.

First, I construct INSERT that embeds the SELECT statement, so it only
happens if the row doesn't already exist.

Second, I check to see if the INSERT fails with "duplicate" error, which
is part of the string that PostgreSQL returns if you try to insert the
same primary key twice.

Seem reasonable? Here's the key code, which is executed
in a larger eval{} block, with RaiseError = 1;

    my $sth = $dbh->prepare(
         "INSERT INTO " . $self->table_name . " (a_session,id)  SELECT ?, ?
            WHERE NOT EXISTS (SELECT 1 FROM " . $self->table_name . " 
WHERE id=? LIMIT 1)");

    $sth->bind_param(1,$datastr,{ pg_type => $type });
    $sth->bind_param(2, $sid);
    $sth->bind_param(3, $sid); # in the SELECT statement
    my $rv = '';
    eval { $rv = $sth->execute(); };
    if ( $rv eq '0E0' or (defined $@ and $@ =~ m/duplicate/i) ) {
        my $sth = $dbh->prepare("UPDATE " . $self->table_name . " SET 
a_session=? WHERE id=?");
        $sth->bind_param(1,$datastr,{ pg_type => $type });
        $sth->bind_param(2,$sid);
        $sth->execute;
    }

Thanks!

     Mark



Thread Next


nntp.perl.org: Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at ask@perl.org | Group listing | About