summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCaspar De Groot2011-05-11 05:34:39 (EDT)
committerCaspar De Groot2011-05-11 05:34:39 (EDT)
commitb1fb06ac3a16d1f51ac6b986bc8e07602b180534 (patch)
tree9197cebe017a90f4f52fca3c811497be070fee32
parent057648203be746491fcca57f5760039458656b5c (diff)
downloadcdo-b1fb06ac3a16d1f51ac6b986bc8e07602b180534.zip
cdo-b1fb06ac3a16d1f51ac6b986bc8e07602b180534.tar.gz
cdo-b1fb06ac3a16d1f51ac6b986bc8e07602b180534.tar.bz2
[Bug 297940] CommitTimestamps are not guaranteed to be strictly ordered
https://bugs.eclipse.org/bugs/show_bug.cgi?id=297940
-rw-r--r--plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Repository.java79
-rw-r--r--plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/TimeStampAuthority.java218
-rw-r--r--plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/TransactionCommitContext.java9
-rw-r--r--plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalRepository.java25
4 files changed, 274 insertions, 57 deletions
diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Repository.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Repository.java
index bf1f8a0..5d5117b 100644
--- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Repository.java
+++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Repository.java
@@ -86,7 +86,6 @@ import org.eclipse.net4j.util.ReflectUtil.ExcludeFromDump;
import org.eclipse.net4j.util.StringUtil;
import org.eclipse.net4j.util.collection.MoveableList;
import org.eclipse.net4j.util.collection.Pair;
-import org.eclipse.net4j.util.concurrent.ConcurrencyUtil;
import org.eclipse.net4j.util.container.Container;
import org.eclipse.net4j.util.container.IPluginContainer;
import org.eclipse.net4j.util.lifecycle.LifecycleUtil;
@@ -161,11 +160,8 @@ public class Repository extends Container<Object> implements InternalRepository
private List<CDOCommitInfoHandler> commitInfoHandlers = new ArrayList<CDOCommitInfoHandler>();
- @ExcludeFromDump
- private transient long lastCommitTimeStamp;
-
- @ExcludeFromDump
- private transient Object lastCommitTimeStampLock = new Object();
+ // Bugzilla 297940
+ private TimeStampAuthority timeStampAuthority = new TimeStampAuthority(this);
@ExcludeFromDump
private transient Object createBranchLock = new Object();
@@ -758,68 +754,37 @@ public class Repository extends Container<Object> implements InternalRepository
public long getLastCommitTimeStamp()
{
- synchronized (lastCommitTimeStampLock)
- {
- return lastCommitTimeStamp;
- }
+ return timeStampAuthority.getLastFinishedTimeStamp();
}
public void setLastCommitTimeStamp(long lastCommitTimeStamp)
{
- synchronized (lastCommitTimeStampLock)
- {
- if (this.lastCommitTimeStamp < lastCommitTimeStamp)
- {
- this.lastCommitTimeStamp = lastCommitTimeStamp;
- lastCommitTimeStampLock.notifyAll();
- }
- }
+ timeStampAuthority.setLastFinishedTimeStamp(lastCommitTimeStamp);
}
public long waitForCommit(long timeout)
{
- synchronized (lastCommitTimeStampLock)
- {
- try
- {
- lastCommitTimeStampLock.wait(timeout);
- }
- catch (Exception ignore)
- {
- }
-
- return lastCommitTimeStamp;
- }
+ return timeStampAuthority.waitForCommit(timeout);
}
public long[] createCommitTimeStamp(OMMonitor monitor)
{
- monitor.begin();
+ return timeStampAuthority.startCommit(CDOBranchPoint.UNSPECIFIED_DATE, monitor);
+ }
- try
- {
- long now = getTimeStamp();
- synchronized (lastCommitTimeStampLock)
- {
- if (lastCommitTimeStamp != 0)
- {
- while (lastCommitTimeStamp >= now)
- {
- ConcurrencyUtil.sleep(1L);
- now = getTimeStamp();
- monitor.checkCanceled();
- }
- }
+ public long[] forceCommitTimeStamp(long override, OMMonitor monitor)
+ {
+ return timeStampAuthority.startCommit(override, monitor);
+ }
- long previousTimeStamp = lastCommitTimeStamp;
- lastCommitTimeStamp = now;
- return new long[] { now, previousTimeStamp };
- }
- }
- finally
- {
- monitor.done();
- }
+ public void endCommit(long timestamp)
+ {
+ timeStampAuthority.endCommit(timestamp);
+ }
+
+ public void failCommit(long timestamp)
+ {
+ timeStampAuthority.failCommit(timestamp);
}
/**
@@ -1430,7 +1395,8 @@ public class Repository extends Container<Object> implements InternalRepository
@Override
protected long[] createTimeStamp(OMMonitor monitor)
{
- return new long[] { store.getCreationTime(), CDOBranchPoint.UNSPECIFIED_DATE };
+ InternalRepository repository = getTransaction().getSession().getManager().getRepository();
+ return repository.forceCommitTimeStamp(store.getCreationTime(), monitor);
}
@Override
@@ -1555,7 +1521,8 @@ public class Repository extends Container<Object> implements InternalRepository
if (!skipInitialization)
{
- lastCommitTimeStamp = Math.max(store.getCreationTime(), store.getLastCommitTime());
+ long lastCommitTimeStamp = Math.max(store.getCreationTime(), store.getLastCommitTime());
+ timeStampAuthority.setLastFinishedTimeStamp(lastCommitTimeStamp);
initMainBranch(branchManager, lastCommitTimeStamp);
if (store.isFirstStart())
diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/TimeStampAuthority.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/TimeStampAuthority.java
new file mode 100644
index 0000000..d9a9a0a
--- /dev/null
+++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/TimeStampAuthority.java
@@ -0,0 +1,218 @@
+/**
+ * Copyright (c) 2004 - 2009 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Caspar De Groot - initial API and implementation
+ */
+package org.eclipse.emf.cdo.internal.server;
+
+import org.eclipse.emf.cdo.common.branch.CDOBranchPoint;
+import org.eclipse.emf.cdo.spi.server.InternalRepository;
+
+import org.eclipse.net4j.util.ReflectUtil.ExcludeFromDump;
+import org.eclipse.net4j.util.concurrent.ConcurrencyUtil;
+import org.eclipse.net4j.util.om.monitor.OMMonitor;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * Bugzilla 297940, 290032
+ *
+ * @author Caspar De Groot
+ */
+class TimeStampAuthority
+{
+ private InternalRepository repository;
+
+ /**
+ * Holds the timestamp that was issued in response to the last call to {@link #createTimestamp()}
+ */
+ @ExcludeFromDump
+ private transient long lastIssuedTimeStamp = CDOBranchPoint.UNSPECIFIED_DATE;
+
+ /**
+ * Holds the timestamp that was last reported finished by a call to {@link #endCommit(long)}
+ */
+ private long lastFinishedTimeStamp;
+
+ private LastCommitTimeStampLock lastCommitTimeStampLock = new LastCommitTimeStampLock();
+
+ private boolean strictOrdering; // TODO (CD) Should be a repo property
+
+ /**
+ * A lock to block on if strict commit ordering is enabled
+ */
+ private StrictOrderingLock strictOrderingLock = new StrictOrderingLock();
+
+ /**
+ * An ordered list of timestamps that have been issued but have not (yet) been reported finished. (It is ordered
+ * because the timestamps are added sequentially.)
+ */
+ private List<Long> runningTransactions = new LinkedList<Long>();
+
+ /**
+ * A set of timestamps that have been reported finished but have not yet been
+ */
+ private SortedSet<Long> finishedTransactions = new TreeSet<Long>();
+
+ TimeStampAuthority(InternalRepository repository)
+ {
+ this.repository = repository;
+ }
+
+ synchronized long[] startCommit(OMMonitor monitor)
+ {
+ return startCommit(CDOBranchPoint.UNSPECIFIED_DATE, monitor);
+ }
+
+ synchronized long[] startCommit(long timeStampOverride, OMMonitor monitor)
+ {
+ monitor.begin();
+
+ if (strictOrdering)
+ {
+ strictOrderingLock.lock();
+ }
+
+ try
+ {
+ long now = repository.getTimeStamp();
+ if (lastIssuedTimeStamp != CDOBranchPoint.UNSPECIFIED_DATE)
+ {
+ while (lastIssuedTimeStamp == now)
+ {
+ ConcurrencyUtil.sleep(1);
+ now = repository.getTimeStamp();
+ monitor.checkCanceled();
+ }
+ }
+
+ if (timeStampOverride != CDOBranchPoint.UNSPECIFIED_DATE)
+ {
+ now = timeStampOverride;
+ }
+
+ long previousIssuedTimeStamp = lastIssuedTimeStamp;
+ lastIssuedTimeStamp = now;
+
+ runningTransactions.add(lastIssuedTimeStamp);
+ return new long[] { lastIssuedTimeStamp, previousIssuedTimeStamp };
+ }
+ finally
+ {
+ monitor.done();
+ }
+ }
+
+ synchronized void endCommit(long timeStamp)
+ {
+ if (!runningTransactions.remove(timeStamp))
+ {
+ throw new IllegalArgumentException("Cannot end transaction with unknown timestamp " + timeStamp);
+ }
+
+ finishedTransactions.add(timeStamp);
+
+ // We can remove a timestamp from finishedTransactions if it is smaller (i.e. older) than any
+ // of the runningTransactions. Since both sets are sorted, we only need to compare the heads.
+ long oldestRunning = runningTransactions.isEmpty() ? Long.MAX_VALUE : runningTransactions.get(0);
+ long oldestFinished;
+ while (!finishedTransactions.isEmpty() && (oldestFinished = finishedTransactions.first()) < oldestRunning)
+ {
+ finishedTransactions.remove(oldestFinished);
+ lastFinishedTimeStamp = oldestFinished;
+ }
+
+ if (strictOrdering)
+ {
+ strictOrderingLock.unlock();
+ }
+ }
+
+ synchronized void failCommit(long timeStamp)
+ {
+ if (!runningTransactions.remove(timeStamp))
+ {
+ throw new IllegalArgumentException("Cannot fail transaction with unknown timestamp " + timeStamp);
+ }
+
+ if (strictOrdering)
+ {
+ strictOrderingLock.unlock();
+ }
+ }
+
+ synchronized long getLastFinishedTimeStamp()
+ {
+ if (lastFinishedTimeStamp != 0)
+ {
+ return lastFinishedTimeStamp;
+ }
+
+ // If we get here, no commit has finished since the server was started
+ if (lastIssuedTimeStamp == 0) // No commit has started either
+ {
+ // We can safely return the current system time minus one milli.
+ return repository.getTimeStamp() - 1;
+ }
+
+ // If we get here, one or more commits are running
+ // We can safely return the start time of the longest-running, minus one milli.
+ return runningTransactions.get(0) - 1;
+ }
+
+ long waitForCommit(long timeout)
+ {
+ synchronized (lastCommitTimeStampLock)
+ {
+ try
+ {
+ lastCommitTimeStampLock.wait(timeout);
+ }
+ catch (Exception ignore)
+ {
+ }
+
+ return lastFinishedTimeStamp;
+ }
+ }
+
+ void setLastFinishedTimeStamp(long lastCommitTimeStamp)
+ {
+ synchronized (lastCommitTimeStampLock)
+ {
+ if (lastFinishedTimeStamp < lastCommitTimeStamp)
+ {
+ lastFinishedTimeStamp = lastCommitTimeStamp;
+ lastCommitTimeStampLock.notifyAll();
+ }
+ }
+ }
+
+ /**
+ * A separate class for better monitor debugging.
+ *
+ * @author Eike Stepper
+ */
+ private static final class LastCommitTimeStampLock
+ {
+ }
+
+ /**
+ * A separate class for better monitor debugging.
+ *
+ * @author Eike Stepper
+ */
+ private static final class StrictOrderingLock extends ReentrantLock
+ {
+ private static final long serialVersionUID = 1L;
+ }
+}
diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/TransactionCommitContext.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/TransactionCommitContext.java
index 1435df9..a612927 100644
--- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/TransactionCommitContext.java
+++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/TransactionCommitContext.java
@@ -165,7 +165,7 @@ public class TransactionCommitContext implements InternalCommitContext
public String getUserID()
{
- return getTransaction().getSession().getUserID();
+ return transaction.getSession().getUserID();
}
public String getCommitComment()
@@ -424,6 +424,9 @@ public class TransactionCommitContext implements InternalCommitContext
monitor.begin(101);
accessor.commit(monitor.fork(100));
updateInfraStructure(monitor.fork());
+
+ // Bugzilla 297940
+ repository.endCommit(timeStamp);
}
catch (Throwable ex)
{
@@ -965,6 +968,10 @@ public class TransactionCommitContext implements InternalCommitContext
{
OM.LOG.warn("Problem while rolling back the transaction", ex); //$NON-NLS-1$
}
+ finally
+ {
+ repository.failCommit(timeStamp);
+ }
}
}
}
diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalRepository.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalRepository.java
index 8250ed4..60b1f16 100644
--- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalRepository.java
+++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalRepository.java
@@ -107,6 +107,31 @@ public interface InternalRepository extends IRepository, PackageProcessor, Packa
public long[] createCommitTimeStamp(OMMonitor monitor);
/**
+ * Like {@link #createCommitTimeStamp(OMMonitor)}, but forces the repository to use the timestamp value passed in as
+ * the argument. This should be called only to force the timestamp of the first commit of a new repository to be equal
+ * to its creation time.
+ *
+ * @since 4.0
+ */
+ public long[] forceCommitTimeStamp(long timestamp, OMMonitor monitor);
+
+ /**
+ * Notifies the repository of the completion of a commit. The value passed in must be a value obtained earlier through
+ * {@link #createCommitTimeStamp(OMMonitor)}
+ *
+ * @since 4.0
+ */
+ public void endCommit(long timeStamp);
+
+ /**
+ * Notifies the repository of the failure of a commit. The value passed in must be a value obtained earlier through
+ * {@link #createCommitTimeStamp(OMMonitor)}
+ *
+ * @since 4.0
+ */
+ public void failCommit(long timeStamp);
+
+ /**
* @since 4.0
*/
public void sendCommitNotification(InternalSession sender, CDOCommitInfo commitInfo);