[jboss-user] [JBoss Cache: Core Edition] - Deadlock in JDBCCacheLoader

gpklimek do-not-reply at jboss.com
Thu Jan 8 12:49:03 EST 2009


It is possible to have a deadlock in JDBCCacheLoader when concurrently trying to put two different entries into it. Timing has to be right of course and even then chances are small (like 1 to 16384). But still it is a problem.
I have found it in version 1.4.1.SP8 but it is still a problem in 3.0.1.GA

I have put together a unit test (for 3.0.1.GA) which demonstrates the issue: trying to put Fqns "/a/b" and "/c/d" at the same time is fine, but "/a/m" and "/l/p" is not


  | import static org.easymock.EasyMock.*;
  | 
  | import java.sql.Connection;
  | import java.sql.DatabaseMetaData;
  | import java.sql.PreparedStatement;
  | import java.sql.ResultSet;
  | import java.util.Hashtable;
  | import java.util.Properties;
  | 
  | import javax.naming.Context;
  | import javax.naming.NamingException;
  | import javax.naming.spi.InitialContextFactory;
  | import javax.sql.DataSource;
  | 
  | import junit.framework.TestCase;
  | 
  | import org.easymock.Capture;
  | import org.easymock.IAnswer;
  | import org.jboss.cache.CacheSPI;
  | import org.jboss.cache.Fqn;
  | import org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig;
  | import org.jboss.cache.io.ByteBuffer;
  | import org.jboss.cache.loader.JDBCCacheLoader;
  | import org.jboss.cache.marshall.Marshaller;
  | 
  | public class JDBCCacheLoaderTest extends TestCase {
  | 	private static final long EXECUTE_QUERY_DELAY = 1000;
  | 	private static final long MAX_TIME_FOR_PUT = 10000;
  | 	
  | 	private static Context context;
  | 
  | 	public static class SimpleContextFactory implements InitialContextFactory {
  | 		public Context getInitialContext(Hashtable<?, ?> environment) throws NamingException {
  | 			return context;
  | 		}
  | 	}
  | 
  | 	private JDBCCacheLoader jdbcCacheLoader;
  | 
  | 	@Override
  | 	protected void setUp() throws Exception {
  | 		System.setProperty("java.naming.factory.initial", "JDBCCacheLoaderTest$SimpleContextFactory");
  | 
  | 		//JDBC mocks
  | 		context = createNiceMock("context", Context.class);
  | 		final CacheSPI cacheSPI = createNiceMock("cacheSPI", CacheSPI.class);
  | 		final Marshaller marshaller = createNiceMock("marshaller", Marshaller.class);
  | 		final DataSource dataSource = createNiceMock("dataSource", DataSource.class);
  | 		final Connection connection = createNiceMock("connection", Connection.class);
  | 		final DatabaseMetaData databaseMetaData = createNiceMock("databaseMetaData", DatabaseMetaData.class);
  | 		final PreparedStatement preparedStatement = createNiceMock("preparedStatement", PreparedStatement.class);
  | 		final ResultSet resultSet = createNiceMock("resultSet", ResultSet.class);
  | 		makeThreadSafe(cacheSPI, true);
  | 		makeThreadSafe(marshaller, true);
  | 		makeThreadSafe(dataSource, true);
  | 		makeThreadSafe(connection, true);
  | 		makeThreadSafe(databaseMetaData, true);
  | 		makeThreadSafe(preparedStatement, true);
  | 		makeThreadSafe(resultSet, true);
  | 
  | 		//mock expectations
  | 		expect(context.lookup("java:/JDBCCacheLoader")).andStubReturn(dataSource);
  | 		expect(cacheSPI.getMarshaller()).andStubReturn(marshaller);
  | 		expect(marshaller.objectToBuffer(capture(new Capture<Object>()))).andStubReturn(new ByteBuffer(new byte[] {'a'}, 0, 1));
  | 		expect(dataSource.getConnection()).andStubReturn(connection);
  | 		expect(connection.getMetaData()).andStubReturn(databaseMetaData);
  | 		expect(databaseMetaData.getDriverName()).andStubReturn("a driver name");
  | 		expect(connection.prepareStatement(capture(new Capture<String>()))).andStubReturn(preparedStatement);
  | 		expect(preparedStatement.executeQuery()).andStubAnswer(new IAnswer<ResultSet>() {
  | 			public ResultSet answer() throws Throwable {
  | 				Thread.sleep(EXECUTE_QUERY_DELAY);
  | 				return resultSet;
  | 			}
  | 		});
  | 		preparedStatement.executeUpdate();
  | 		expectLastCall().andStubReturn(1);
  | 
  | 		replay(context, cacheSPI, marshaller, dataSource, connection, databaseMetaData, preparedStatement, resultSet);
  | 
  | 		//JDBCCacheLoader init
  | 		Properties props = new Properties();
  | 		props.put("cache.jdbc.table.name","TREE_CACHE");
  | 		props.put("cache.jdbc.table.create","false");
  | 		props.put("cache.jdbc.fqn.column","FQN");
  | 		props.put("cache.jdbc.node.column","NODE");
  | 		props.put("cache.jdbc.datasource","java:/JDBCCacheLoader");
  | 
  | 		IndividualCacheLoaderConfig config = new IndividualCacheLoaderConfig();
  | 		config.setProperties(props);
  | 
  | 		jdbcCacheLoader = new JDBCCacheLoader();
  | 		jdbcCacheLoader.setConfig(config);
  | 		jdbcCacheLoader.setCache(cacheSPI);
  | 		jdbcCacheLoader.start();
  | 	}
  | 	
  | 	public void testPutCaseDeadlock() throws Exception {
  | 		Thread thread1 = new Thread(new Putter("/a/m"));
  | 		Thread thread2 = new Thread(new Putter("/l/p"));
  | 		thread1.start();
  | 		thread2.start();
  | 		thread1.join(MAX_TIME_FOR_PUT);
  | 		thread2.join(MAX_TIME_FOR_PUT);
  | 		assertEquals(Thread.State.TERMINATED, thread1.getState());
  | 		assertEquals(Thread.State.TERMINATED, thread2.getState());
  | 	}
  | 	
  | 	public void testPutCaseNoDeadlock() throws Exception {
  | 		Thread thread1 = new Thread(new Putter("/a/b"));
  | 		Thread thread2 = new Thread(new Putter("/c/d"));
  | 		thread1.start();
  | 		thread2.start();
  | 		thread1.join(MAX_TIME_FOR_PUT);
  | 		thread2.join(MAX_TIME_FOR_PUT);
  | 		assertEquals(Thread.State.TERMINATED, thread1.getState());
  | 		assertEquals(Thread.State.TERMINATED, thread2.getState());
  | 	}
  | 	
  | 	private class Putter implements Runnable {
  | 		private String name;
  | 		
  | 		public Putter(String name) {
  | 			this.name = name;
  | 		}
  | 
  | 		public void run() {
  | 			try {
  | 				jdbcCacheLoader.put(Fqn.fromString(name), "a key", "a value");
  | 			} catch (Exception e) {
  | 				e.printStackTrace();
  | 			}
  | 		}
  | 	}
  | }
  | 

View the original post : http://www.jboss.com/index.html?module=bb&op=viewtopic&p=4200407#4200407

Reply to the post : http://www.jboss.com/index.html?module=bb&op=posting&mode=reply&p=4200407



More information about the jboss-user mailing list