Question

I have a scenario in which I think I need to lock a table against all activity (reads and writes). I am using Propel 1.6.x and intend to write a small extra library to handle the locking.

My use case is a version table that has a primary key of (id, creator, version). The column pair (id, creator) is actually a PK on another table. When a new row is inserted, the MAX(version) is read in PHP code and then inserted into a row class for later saving. Between the max and the save, there is a race condition potential where another process could get the same max value, and then of course one of the inserts would fail due to non-uniqueness.

To avoid the need for locks, I would have preferred to use a subselect for the save, something like:

INSERT INTO
    test_model_test_organiser_versionable
(id, creator, version)
VALUES (
    3,
    1, 
    (
        SELECT COALESCE(MAX(version), 1)
        FROM test_model_test_organiser_versionable
        WHERE id = 3
        AND creator = 1
    )
);

However this isn't supported in Propel, and if I do it through PDO I won't (afaik) have a way of discovering the value given to the 'version' part of the composite PK.

Now, I could have an additional auto-incrementing column, since the auto-incremented value could be read after the insert to look up the row, and hence I can do a reselect in Propel. But I would like to avoid the extraneous column if I can help it - seems less than elegant in my opinion.

So... my feeling is that wrapping the max & save calls in a lock is the way to go. This would have to lock the whole table IMO, and guard against reads as well as writes (otherwise a subsequent max call would not wait, and hence would lead to a non-uniqueness fail again).

I need this to work with all the platforms supported by Propel, so - unless there is a better approach I should take - is there a PHP library that will do this locking against any PDO database? If not, I can do it myself - but it is nice if someone else has done the testing donkey-work against a variety of dbs (set up MSSQL Server? - no thanks :)

Was it helpful?

Solution

I went with locking the table completely whilst the MAX() and INSERT/UPDATE is done. I found these worked:

  • MySQL: LOCK TABLES my_table WRITE
  • PostgreSQL: LOCK TABLE my_table IN SHARE ROW EXCLUSIVE MODE

I plan to look up and test the same for Oracle, MSSQL and SQLite in due course - but the first two are probably the most popular for F/OSS projects.

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