Gitlab Community Edition Instance

Commit f1a664e8 authored by mhellka's avatar mhellka
Browse files

Let DiskTransaction check its state before blocking.

So any but the first of many parallel rollback() or commit() calls
will fail fast and less threads are blocked.
parent 26402201
Pipeline #115158 passed with stages
in 11 minutes and 25 seconds
......@@ -109,110 +109,113 @@ public class DiskTransaction implements UserTransaction, TransactionHandle {
}
@Override
public synchronized void commit() throws TARollbackException {
public void commit() throws TARollbackException {
ensureState(TAState.NEW);
synchronized (this) {
ensureState(TAState.NEW);
try {
for (final TAListener l : listeners)
l.beforeCommit(this);
try {
for (final TAListener l : listeners)
l.beforeCommit(this);
changeState(TAState.PREPARING);
// Actually prepare resources
for (final TAResource resource : boundResources) {
try {
resource.prepare(this);
} catch (final Exception e) {
log.debug("Failed to prepare resource {}", resource, e);
throw e;
}
}
journal.writePrivileged(DTA_SHOULD_COMMIT, null);
journal.close();
changeState(TAState.PREPARING);
} catch (final Exception e) {
rollback(e);
throw new TARollbackException(e);
}
// From now on, errors are fatal and cannot be resolved by a rollback
changeState(TAState.PREPARED);
final List<Exception> errors = new ArrayList<>(0);
// Actually prepare resources
for (final TAResource resource : boundResources) {
try {
resource.prepare(this);
resource.commit(this);
} catch (final Exception e) {
log.debug("Failed to prepare resource {}", resource, e);
throw e;
errors.add(e);
log.error("Failed to commit after successfull prepare (tx={}). "
+ "Resource {} remains in locked state.", id, resource, e);
}
}
journal.writePrivileged(DTA_SHOULD_COMMIT, null);
journal.close();
} catch (final Exception e) {
rollback(e);
throw new TARollbackException(e);
}
// From now on, errors are fatal and cannot be resolved by a rollback
changeState(TAState.PREPARED);
final List<Exception> errors = new ArrayList<>(0);
for (final TAResource resource : boundResources) {
try {
resource.commit(this);
} catch (final Exception e) {
errors.add(e);
log.error("Failed to commit after successfull prepare (tx={}). "
+ "Resource {} remains in locked state.",
id, resource, e);
if (!errors.isEmpty()) {
// PANIC! We have a partially committed transaction and no way
// to solve this problem automatically. Ask for human help. Keep
// writeAheadLog so recovery may be possible. Keep transaction in
// memory.
throw new TAFatalError("Failed commit after successfull prepare. See logs for details.", errors.get(0));
}
}
if (!errors.isEmpty()) {
// PANIC! We have a partially committed transaction and no way
// to solve this problem automatically. Ask for human help. Keep
// writeAheadLog so recovery may be possible. Keep transaction in
// memory.
throw new TAFatalError("Failed commit after successfull prepare. See logs for details.",
errors.get(0));
}
changeState(TAState.COMMITTED);
manager.forget(this);
journal.remove();
changeState(TAState.COMMITTED);
manager.forget(this);
journal.remove();
for (final TAListener l : listeners) {
try {
l.afterCommit(this);
} catch (final Exception e) {
log.warn("Error on after-commit listener (ignored)", e);
for (final TAListener l : listeners) {
try {
l.afterCommit(this);
} catch (final Exception e) {
log.warn("Error on after-commit listener (ignored)", e);
}
}
}
}
@Override
public synchronized void rollback(Throwable reason) {
if (txState == TAState.CLOSED)
return;
public void rollback(Throwable reason) {
if (txState == TAState.CLOSING || txState == TAState.CLOSED)
return; // NOP (closed or already closing in different thread)
changeState(TAState.CLOSING);
journal.remove();
synchronized (this) {
changeState(TAState.CLOSING);
/*
* No need to persist the log here. The previous state is either
* NEW/ROLLBACKONLY or PREPARING. The log is only guaranteed to be
* persisted after PREPARED or if the resource flush()ed the log
* explicitly. If no resource did this, and a prepare() call failed,
* then recovery is optional.
*/
journal.remove();
final List<Exception> errors = new ArrayList<>(0);
for (final TAResource resource : boundResources) {
try {
resource.rollback(this);
} catch (final Exception e) {
log.error("Rollback failed. Affected resources are probably in a dirty state.", e);
// Best we can do is to rollback as much as possible and then
// ask for help.
errors.add(e);
/*
* No need to persist the log here. The previous state is either
* NEW/ROLLBACKONLY or PREPARING. The log is only guaranteed to be persisted
* after PREPARED or if the resource flush()ed the log explicitly. If no
* resource did this, and a prepare() call failed, then recovery is optional.
*/
final List<Exception> errors = new ArrayList<>(0);
for (final TAResource resource : boundResources) {
try {
resource.rollback(this);
} catch (final Exception e) {
log.error("Rollback failed. Affected resources are probably in a dirty state.", e);
// Best we can do is to rollback as much as possible and then
// ask for help.
errors.add(e);
}
}
}
if (!errors.isEmpty()) {
// Not as fatal as a failing commit, because MVCC somewhat
// guarantees that other transactions do not see the dirty state, it
// is still there and should be cleaned up somehow. We leave the
// transaction open, the writeAheadLog alive and exit here.
throw new TAFatalError("Rollback failed. Affected resources are probably in a dirty state.",
errors.get(0));
}
if (!errors.isEmpty()) {
// Not as fatal as a failing commit, because MVCC somewhat
// guarantees that other transactions do not see the dirty state, it
// is still there and should be cleaned up somehow. We leave the
// transaction in a closing state, the writeAheadLog alive and exit here.
// This WILL leak memory :/
throw new TAFatalError("Rollback failed. Affected resources are probably in a dirty state.",
errors.get(0));
}
changeState(TAState.CLOSED);
manager.forget(this);
changeState(TAState.CLOSED);
manager.forget(this);
}
for (final TAListener l : listeners) {
try {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment