I am looking to implement a dirty bit on my Ormlite models. During a sync process with the server I need to make sure that I do not overwrite a row that has been edited by the user. For this purpose I need a way to guarantee that no user edit is allowed between the time of the sync thread reading the dirty bit and updating the values.
I am using a singleton DatabaseHelper class:
public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
private static DatabaseHelper databaseHelper = null;
private Dao<FormField, String> formFieldDao = null;
public static DatabaseHelper getHelper() {
if (databaseHelper == null) {
databaseHelper =
OpenHelperManager.getHelper(MyGoldCare.getAppContext(), DatabaseHelper.class);
}
return databaseHelper;
}
public Dao<FormField, String> getFormFieldDao() throws java.sql.SQLException {
if (formFieldDao == null) {
formFieldDao = getDao(FormField.class);
}
return formFieldDao;
}
My first extinct was to do the read of the dirty bit and update of the values within a transaction. I wrote a simple test to see if this would work without race conditions:
public class TestTransactionWriter implements Runnable {
FormField formField;
long sleepTime;
public TestTransactionWriter(FormField formField, long sleepTime){
this.formField = formField;
this.sleepTime = sleepTime;
}
@Override
public void run() {
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND);
try {
TransactionManager.callInTransaction(DatabaseHelper.getHelper().getConnectionSource(), new Callable<Void>(){
public Void call() throws SQLException,InterruptedException{
for (int i = 0; i < 10; i++) {
DatabaseHelper.getHelper().getFormFieldDao().refresh(formField);
Thread.sleep(sleepTime);
Integer v = Integer.parseInt(formField.getValue());
v++;
formField.setValue(v.toString());
DatabaseHelper.getHelper().getFormFieldDao().update(formField);
}
return null;
}
});
}catch (SQLException se){
Log.e("TEST",se.toString());
}
}
}
Then I started 2 threads with the above runnable:
formField.setValue("0");
DatabaseHelper.getHelper().getFormFieldDao().update(formField);
TestTransactionWriter testTransactionWriter = new TestTransactionWriter(formField,1500);
Thread thread = new Thread(testTransactionWriter) ;
thread.start();
TestTransactionWriter testTransactionWriter1 = new TestTransactionWriter(formField,2000);
Thread thread1 = new Thread(testTransactionWriter) ;
thread1.start();
}catch (SQLException se){
Log.e(LOG_TAG,se.toString());
}
}
I got the result I was expecting. The final value was 20 every time I tested. When I remove the call in transaction I get race conditions. Another thing I noticed is that while these threads are running I am unable to read any values(other tables as well) from the database and the UI freezes for a moment.
To confirm my test I decided to read through the source of Ormlite and i found the following warning:
* WARNING: it is up to you to properly synchronize around this method if multiple threads are using a
* connection-source which works gives out a single-connection. The reason why this is necessary is that multiple
* operations are performed on the connection and race-conditions will exist with multiple threads working on the
* same connection.
* </p>
*
* @param callable
* Callable to execute inside of the transaction.
* @return The object returned by the callable.
* @throws SQLException
* If the callable threw an exception then the transaction is rolled back and a SQLException wraps the
* callable exception and is thrown by this method.
*/
public <T> T callInTransaction(final Callable<T> callable) throws SQLException {
return callInTransaction(connectionSource, callable);
How is it that in my case the test did not produce race conditions even though I did no synchronization of my own? Was it some SQLlite database lock kicking in? Was there synchronization at some other level given the face I use a singleton database helper?
How is it that in my case the test did not produce race conditions even though I did no synchronization of my own? Was it some SQLlite database lock kicking in?
No but I suspect that some Sqlite locking kicked in. That WARNING is for all ORMLite backends including JDBC which allow multiple thread connections to the same database.
For this purpose I need a way to guarantee that no user edit is allowed between the time of the sync thread reading the dirty bit and updating the values.
I'd take a look at the version = true
support in ORMLite which does validation of this at the data level. It is made for distributed systems where multiple connections are being made to a database from multiple clients but it might help you as well. See the version field documentation.