http://www.perlmonks.org?node_id=663983

matija has asked for the wisdom of the Perl Monks concerning the following question:

I'm writing a Dbix::Class model which I expect will come under a considerable load.

Part of the operations the model will have to perform involves stuff that has to happen in a transaction, and as I am really getting into the write-tests-first way of programming, I want to think up some tests that will verify that the transactions are done right, and that the reverts are handled correctly.

That means that I need to trigger the race conditions "on demand". I've been reading Testing Race Conditions but the responses don't seem to help what I'm trying to test - they would help detect if there are unhandled race conditions, while I want to, first, check that predictable race conditions are properly handled. (And only then go to probabilistic testing if there are any unanticipated race conditions).

Does anybody have ideas on how I could test predictable race conditions (i.e. "what happens if another request comes in just as I come to this point") without having to modify the source I am testing?

Failing that, what would you consider the least intrusive (and the least time consuming in production) modification of the source that would enable such testing?

Replies are listed 'Best First'.
Re: Testing transactions
by roboticus (Chancellor) on Jan 24, 2008 at 12:18 UTC
    matija:

    For a simple method, have your test code open *two* handles to the database. Then start the first transaction on one item, then start the second transaction, and switch back and forth as desired.

    Slightly better might be two create two threads or processes and insert some time delays at various points to "usually" interleave the bits you want to test. (Usually because it won't be deterministic, as timing variations will come into play. But usually this is good enough.)

    If "good enough" isn't good enough ;^), you could always add mutexes, semaphores, or even directory creation/removal to enforce interleaving.

    However, I haven't used Dbix::Class before, so I don't know if anything in it would invalidate what I said. I'm also assuming that your transactions have multiple steps that you control.

    The techniques I suggested won't work for testing transactions that use a single call to the database. For that, you may want to write your own DBD class so you can monitor which actions occur when, and put in your own delays and/or semaphores or state variables to encourage actions to occur in the desired order for testing.

    I hope this is useful....

    ...roboticus

Re: Testing transactions
by kyle (Abbot) on Jan 24, 2008 at 16:37 UTC

    When I was first learning about transactions, I wrote a test script to understand how they behave. I'm posting here a stripped down version of that script.

    What you might find most useful to what you're doing is one of the last tests in there. In it, I start a transaction on one database connection and then fork to try something on another database connection. The parent sleeps to ensure that the child has a chance to try what it's trying. This is not exactly a "race condition", but it does show what happens when two transactions want the same resource at the same time—one of them waits. In the particular case that I tested, they were both trying to insert the same value in a unique field. The child transaction had to wait to see if the parent committed before it knew if it could succeed.

    I did this testing with PostgreSQL (DBD::Pg). When I run the original script, every test passes, but again, what I'm posting here is modified in various ways, some more obvious than others.

Re: Testing transactions
by mpeppler (Vicar) on Jan 24, 2008 at 14:12 UTC
    As roboticus says - open two or more database connections (or client apps) and run requests with varying degrees of delays.

    I presume that the transactional part of the system is handled at the database level. Even so, if Dbix::Class offers a high level of abstraction over what actually happens in the DB (something I don't know as I haven't used it) then you should be careful of things like deadlock situations where locks on different resources are acquired in a different order in two processes, and each process is waiting to acquire a lock that the other already holds.

    And keep in mind that you are likely to find race conditions in places where you didn't think that they were likely to appear...

    Michael