Bugfix: store() should never throw a DuplicateKeyException.

It would be extremely rare, but if two nodes were trying to simultaneously
store() the same record with the same Key, one could fail with a DKE.

Attempt a second 'update' if the insert fails.
This commit is contained in:
Ray Greenwell
2013-05-07 00:03:05 +00:00
parent c63ce0c10e
commit ce3a340199
@@ -553,43 +553,67 @@ public abstract class DepotRepository
} }
final boolean[] created = new boolean[1]; final boolean[] created = new boolean[1];
_ctx.invoke(new CachingModifier<T>(record, key, key) { try {
@Override _ctx.invoke(new CachingModifier<T>(record, key, key) {
protected int invoke (Connection conn, DatabaseLiaison liaison) throws SQLException { @Override
if (_key != null) { protected int invoke (Connection conn, DatabaseLiaison liaison)
// run the update throws SQLException
int mods = builder.prepare(conn).executeUpdate(); {
if (mods > 0) { if (_key != null) {
// if it succeeded, we're done // run the update
return mods; int mods = builder.prepare(conn).executeUpdate();
if (mods > 0) {
// if it succeeded, we're done
return mods;
}
} }
}
// if the update modified zero rows or the primary key was unset, insert // if the update modified zero rows or the primary key was unset, insert
Set<String> identityFields = Collections.emptySet(); Set<String> identityFields = Collections.emptySet();
if (_key == null) { if (_key == null) {
// first, set any auto-generated column values // first, set any auto-generated column values
identityFields = marsh.generateFieldValues(conn, liaison, _result, false); identityFields = marsh.generateFieldValues(conn, liaison, _result, false);
// update our modifier's key so that it can cache our results // update our modifier's key so that it can cache our results
updateKey(marsh.getPrimaryKey(_result, false)); updateKey(marsh.getPrimaryKey(_result, false));
} }
builder.newQuery(new InsertClause(pClass, _result, identityFields)); builder.newQuery(new InsertClause(pClass, _result, identityFields));
int mods = builder.prepare(conn).executeUpdate(); int mods = builder.prepare(conn).executeUpdate();
// run any post-factum value generators and potentially generate our key // run any post-factum value generators and potentially generate our key
if (_key == null) { if (_key == null) {
marsh.generateFieldValues(conn, liaison, _result, true); marsh.generateFieldValues(conn, liaison, _result, true);
updateKey(marsh.getPrimaryKey(_result, false)); updateKey(marsh.getPrimaryKey(_result, false));
}
created[0] = true;
return mods;
} }
created[0] = true; @Override
return mods; public void updateStats (Stats stats) {
stats.noteModification(pClass);
}
});
} catch (DuplicateKeyException dke) {
if (key == null) {
throw dke; // how would this even happen?
} }
@Override // this should be very rare: the insert failed. Retry one more update.
public void updateStats (Stats stats) { _ctx.invoke(new CachingModifier<T>(record, key, key) {
stats.noteModification(pClass); @Override
} protected int invoke (Connection conn, DatabaseLiaison liaison)
}); throws SQLException
{
builder.newQuery(update);
return builder.prepare(conn).executeUpdate();
}
@Override
public void updateStats (Stats stats) {
stats.noteModification(pClass);
}
});
}
return created[0]; return created[0];
} }