Question

I have an issue where I'm running a web service which is performing a transaction involving two tables Users and Transactions. The trouble is that when I select from Transactions it sometimes is unable to find the latest row, which I'm able to see exists in the database.

I'm using Perl/Dancer as the web framework, although I think the issue I'm having is at the database level (I am using MySQL/InnoDB). The pseudocode looks like this:

my $dbh = DBI->connect_cached(("DBI:mysql:Database;host=localhost;mysql_ssl=1",
                 "user", "password",
                 {RaiseError => 0, AutoCommit => 0});
my $sth_transact = $dbh->prepare("select balance from Users ".
                  "where id=? for update");

... store result in local variable ...

my $sth_inner = $dbh->prepare("select deposit from Transactions ".
                   "where id=?");

$sth_inner->execute();
if (my $deposit = $sth_inner->fetch_row()) {
     $deposit *= $bonus;

     $sth_inner = $dbh->prepare("update Transactions set deposit=?".
                                "where id=?");
     $sth_inner->bind_param(1, $deposit);
     $sth_inner->bind_param(2, $transaction_id);
     $sth_inner->execute();

     ... update balance in Users table ...
}

On the first few requests the select on the Transactions table returns the most recent row. However, on the third request, it can no longer find the most recent row. And when I do:

my $sth_inner = $dbh->prepare("select id from Transactions where id=(SELECT max(id) from Transactions)");

It returns the id of 3 rows older than the most recent row. E.g., if there were 87 rows in the Transactions table, it would return 84.

I'm not sure if I'm handling the locking incorrectly. I'm protecting the Users table with a select ... for update lock, but I'm not doing that for the Transactions table. I'm not sure if that's important. Since I nested the update to the Transactions table in the select ... for update of the Users table I thought it would be protected in the transaction.

Any help on why the select on the Transactions table isn't returning the most recent rows is appreciated.

Was it helpful?

Solution

This problem is usually caused by the default isolation level for InnoDB, which is "repeatable read." This means a connection can't see anything added to the database after the current transaction was started.

You can avoid this problem by changing the isolation level (possibly to "read committed" which is the default for Oracle) or by issuing a commit just before your select to begin a new transaction.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top