提交 5f05037a authored 作者: Noel Grandin's avatar Noel Grandin

issue#143 fix ABBA deadlock when flushing sequences

上级 17a573c9
...@@ -14,6 +14,7 @@ import java.util.HashSet; ...@@ -14,6 +14,7 @@ import java.util.HashSet;
import java.util.Properties; import java.util.Properties;
import java.util.Set; import java.util.Set;
import java.util.StringTokenizer; import java.util.StringTokenizer;
import org.h2.api.DatabaseEventListener; import org.h2.api.DatabaseEventListener;
import org.h2.api.ErrorCode; import org.h2.api.ErrorCode;
import org.h2.api.JavaObjectSerializer; import org.h2.api.JavaObjectSerializer;
...@@ -894,6 +895,10 @@ public class Database implements DataHandler { ...@@ -894,6 +895,10 @@ public class Database implements DataHandler {
return wasLocked; return wasLocked;
} }
public void unlockMeta(Session session) {
meta.unlock(session);
}
/** /**
* Remove the given object from the meta data. * Remove the given object from the meta data.
* *
...@@ -2344,6 +2349,10 @@ public class Database implements DataHandler { ...@@ -2344,6 +2349,10 @@ public class Database implements DataHandler {
return meta == null || meta.isLockedExclusively(); return meta == null || meta.isLockedExclusively();
} }
public boolean isSysTableLockedBy(Session session) {
return meta == null || meta.isLockedExclusivelyBy(session);
}
/** /**
* Open a new connection or get an existing connection to another database. * Open a new connection or get an existing connection to another database.
* *
......
...@@ -33,6 +33,10 @@ public class Sequence extends SchemaObjectBase { ...@@ -33,6 +33,10 @@ public class Sequence extends SchemaObjectBase {
private long maxValue; private long maxValue;
private boolean cycle; private boolean cycle;
private boolean belongsToTable; private boolean belongsToTable;
/**
* The last valueWithMargin we flushed. We do a little dance with this to avoid an ABBA deadlock.
*/
private long lastFlushValueWithMargin;
/** /**
* Creates a new sequence for an auto-increment column. * Creates a new sequence for an auto-increment column.
...@@ -240,11 +244,15 @@ public class Sequence extends SchemaObjectBase { ...@@ -240,11 +244,15 @@ public class Sequence extends SchemaObjectBase {
* @param session the session * @param session the session
* @return the next value * @return the next value
*/ */
public synchronized long getNext(Session session) { public long getNext(Session session) {
boolean needsFlush = false; boolean needsFlush = false;
long retVal;
long flushValueWithMargin = -1;
synchronized(this) {
if ((increment > 0 && value >= valueWithMargin) || if ((increment > 0 && value >= valueWithMargin) ||
(increment < 0 && value <= valueWithMargin)) { (increment < 0 && value <= valueWithMargin)) {
valueWithMargin += increment * cacheSize; valueWithMargin += increment * cacheSize;
flushValueWithMargin = valueWithMargin;
needsFlush = true; needsFlush = true;
} }
if ((increment > 0 && value > maxValue) || if ((increment > 0 && value > maxValue) ||
...@@ -252,17 +260,19 @@ public class Sequence extends SchemaObjectBase { ...@@ -252,17 +260,19 @@ public class Sequence extends SchemaObjectBase {
if (cycle) { if (cycle) {
value = increment > 0 ? minValue : maxValue; value = increment > 0 ? minValue : maxValue;
valueWithMargin = value + (increment * cacheSize); valueWithMargin = value + (increment * cacheSize);
flushValueWithMargin = valueWithMargin;
needsFlush = true; needsFlush = true;
} else { } else {
throw DbException.get(ErrorCode.SEQUENCE_EXHAUSTED, getName()); throw DbException.get(ErrorCode.SEQUENCE_EXHAUSTED, getName());
} }
} }
retVal = value;
value += increment;
}
if (needsFlush) { if (needsFlush) {
flush(session); flush(session, flushValueWithMargin);
} }
long v = value; return retVal;
value += increment;
return v;
} }
/** /**
...@@ -271,7 +281,7 @@ public class Sequence extends SchemaObjectBase { ...@@ -271,7 +281,7 @@ public class Sequence extends SchemaObjectBase {
public void flushWithoutMargin() { public void flushWithoutMargin() {
if (valueWithMargin != value) { if (valueWithMargin != value) {
valueWithMargin = value; valueWithMargin = value;
flush(null); flush(null, valueWithMargin);
} }
} }
...@@ -280,24 +290,33 @@ public class Sequence extends SchemaObjectBase { ...@@ -280,24 +290,33 @@ public class Sequence extends SchemaObjectBase {
* *
* @param session the session * @param session the session
*/ */
public synchronized void flush(Session session) { public void flush(Session session, long flushValueWithMargin) {
if (session == null || !database.isSysTableLocked()) { if (session == null || !database.isSysTableLockedBy(session)) {
// This session may not lock the sys table (except if it already has // This session may not lock the sys table (except if it already has
// locked it) because it must be committed immediately, otherwise // locked it) because it must be committed immediately, otherwise
// other threads can not access the sys table. // other threads can not access the sys table.
Session sysSession = database.getSystemSession(); Session sysSession = database.getSystemSession();
synchronized (sysSession) { synchronized (sysSession) {
flushInternal(sysSession); flushInternal(sysSession, flushValueWithMargin);
sysSession.commit(false); sysSession.commit(false);
} }
} else { } else {
synchronized (session) { synchronized (session) {
flushInternal(session); flushInternal(session, flushValueWithMargin);
} }
} }
} }
private void flushInternal(Session session) { private void flushInternal(Session session, long flushValueWithMargin) {
final boolean metaWasLocked = database.lockMeta(session);
synchronized (this) {
if (flushValueWithMargin == lastFlushValueWithMargin) {
if (!metaWasLocked) {
database.unlockMeta(session);
}
return;
}
}
// just for this case, use the value with the margin for the script // just for this case, use the value with the margin for the script
long realValue = value; long realValue = value;
try { try {
...@@ -308,6 +327,12 @@ public class Sequence extends SchemaObjectBase { ...@@ -308,6 +327,12 @@ public class Sequence extends SchemaObjectBase {
} finally { } finally {
value = realValue; value = realValue;
} }
synchronized (this) {
lastFlushValueWithMargin = flushValueWithMargin;
}
if (!metaWasLocked) {
database.unlockMeta(session);
}
} }
/** /**
......
...@@ -347,7 +347,7 @@ public class Column { ...@@ -347,7 +347,7 @@ public class Column {
if (update) { if (update) {
sequence.modify(now + inc, null, null, null); sequence.modify(now + inc, null, null, null);
session.setLastIdentity(ValueLong.get(now)); session.setLastIdentity(ValueLong.get(now));
sequence.flush(session); sequence.flush(session, 0);
} }
} }
} }
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论