views:

194

answers:

1

I am trying to create a database deadlock and I am using JUnit. I have two concurrent tests running which are both updating the same row in a table over and over again in a loop.

My idea is that you update say row A in Table A and then row B in Table B over and over again in one test. Then at the same time you update row B table B and then row A Table A over and over again. From my understanding this should eventually result in a deadlock.

Here is the code For the first test.

public static void testEditCC()
{
    try{
        int rows = 0;
        int counter = 0;
        int large=10000000;
        Connection c=DataBase.getConnection();
        while(counter<large)
        {
            int pid = 87855;
            int cCode = 655;
            String newCountry="Egypt";              
            int bpl = 0;
            stmt = c.createStatement();

            rows = stmt.executeUpdate("UPDATE main " +              //create lock on main table
                                                  "SET BPL="+cCode+
                                                  "WHERE ID="+pid);
            rows = stmt.executeUpdate("UPDATE BPL SET DESCRIPTION='SomeWhere' WHERE ID=602"); //create lock on bpl table
            counter++;
        }

        assertTrue(rows == 1);
        //rows = stmt.executeUpdate("Insert into BPL (ID, DESCRIPTION) VALUES ("+cCode+", '"+newCountry+"')");

    }
    catch(SQLException ex)
    {
        ex.printStackTrace();
        //ex.getMessage();
    }
}

And here is the code for the second test.

public static void testEditCC()
{
    try{
        int rows = 0;
        int counter = 0;
        int large=10000000;
        Connection c=DataBase.getConnection();
        while(counter<large)
        {
            int pid = 87855;
            int cCode = 655;
            String newCountry="Jordan";         
            int bpl = 0;
            stmt = c.createStatement();
            //stmt.close();
            rows = stmt.executeUpdate("UPDATE BPL SET DESCRIPTION='SomeWhere' WHERE ID=602"); //create lock on bpl table
            rows = stmt.executeUpdate("UPDATE main " +          //create lock on main table
                                                  "SET BPL="+cCode+
                                                  "WHERE ID="+pid);
            counter++;
        }

        assertTrue(rows == 1);
        //rows = stmt.executeUpdate("Insert into BPL (ID, DESCRIPTION) VALUES ("+cCode+", '"+newCountry+"')");

    }
    catch(SQLException ex)
    {
        ex.printStackTrace();
    }
}

I am running these two separate JUnit tests at the same time and am connecting to an apache Derby database that I am running in network mode within Eclipse. Can anyone help me figure out why a deadlock is not occurring? Perhaps I am using JUnit wrong.

+1  A: 

You should check the transaction isolation level, as it determines whether or not the DB locks rows touched by a transaction. If the isolation level is too low, no locking occurs, so no deadlock either.

Update: according to this page, the default tx isolation level for Derby is read committed, which should be OK. The page is worth reading btw, as it explains tx isolation and its different levels, and what problems it solves.

Next question then: what is DataBase in your code? This seems to be a nonstandard way to get a connection.

Update2: I think I got it. Quote from the API doc:

Note: By default a Connection object is in auto-commit mode, which means that it automatically commits changes after executing each statement. If auto-commit mode has been disabled, the method commit must be called explicitly in order to commit changes; otherwise, database changes will not be saved.

In other words, rows are not locked because your effective transactions last only for the lifetime of individual updates. You should switch off autocommit before starting to work with your connection:

Connection c=DataBase.getConnection();
c.setAutoCommit(false);
Péter Török
Ok, so it must be the transactions I am doing are not the type that cause deadlocks. I am not sure what else to try, so I will have to do some more reading.
Isawpalmetto
I have a class that I called 'DataBase' that has all my methods in it. One of them is getConnection. It just uses the DriverManager.getConnection(url) method from JDBC.
Isawpalmetto
@Isawpalmetto OK, I think I got it - see my last update.
Péter Török
There we go, now I am getting them, thanks a lot.
Isawpalmetto
Update: What I am getting is actually just a lock timeout. A deadlock is not happening, and I am pretty sure it is not the code because I have tried various deadlock examples from the internet. It must be some settings with JUnit.
Isawpalmetto
@Isawpalmetto Or the DB. I guess the DB detects that the lock can not be acquired and interrupts the transaction after a certain amount of time passed. I observed the same some time ago.
Péter Török
@Isawpalmetto And this is indeed the case with Derby - see http://db.apache.org/derby/docs/10.0/manuals/develop/develop75.html
Péter Török
@Isawpalmetto One more note: in order to achieve a proper deadlock (where thread A and B wait for each other indefinitely), you should ensure that test 1 gets to lock row 1 and test 2 locks row 2 first. With your current setup it is IMHO unlikely: what probably happens is that one of the connections gets _both_ locks and then runs on happily, while the other blocks. If you start both tests _exactly_ the same time, you have _some_ chance of achieving deadlock. To increase your chances, you could insert a sleep period (of e.g. 10 seconds) after the first `executeUpdate` in both tests.
Péter Török
I actually changed the tests from the one that I had posted and was still having the same problem. Then I was thinking that maybe the timeOut lock I was getting was because the updates were happening too fast so that the other thread couldn't even get a lock on the row. I added the sleep period and now I am getting deadlocks. Thanks
Isawpalmetto