diff options
9 files changed, 359 insertions, 46 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 b0b360f4be..9c671fc2a8 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 @@ -974,7 +974,8 @@ public class Repository extends Container<Object> implements InternalRepository public void sendCommitNotification(InternalSession sender, CDOCommitInfo commitInfo, boolean clearResourcePathCache) { - if (!commitInfo.isEmpty()) + boolean isFailureCommitInfo = commitInfo.getBranch() == null; + if (isFailureCommitInfo || !commitInfo.isEmpty()) { sessionManager.sendCommitNotification(sender, commitInfo, clearResourcePathCache); commitInfoManager.notifyCommitInfoHandlers(commitInfo); 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 index b31401ef8c..4ecc4b79e1 100644 --- 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 @@ -100,10 +100,11 @@ class TimeStampAuthority now = timeStampOverride; } + long previousTimeStamp = lastIssuedTimeStamp; lastIssuedTimeStamp = now; - runningTransactions.add(lastIssuedTimeStamp); - return new long[] { lastIssuedTimeStamp, getLastFinishedTimeStamp() }; + runningTransactions.add(now); + return new long[] { now, previousTimeStamp }; } finally { 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 6dbe4b310f..c7ac0db18c 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 @@ -78,6 +78,7 @@ import org.eclipse.net4j.util.io.ExtendedDataInputStream; import org.eclipse.net4j.util.lifecycle.LifecycleUtil; import org.eclipse.net4j.util.om.monitor.Monitor; import org.eclipse.net4j.util.om.monitor.OMMonitor; +import org.eclipse.net4j.util.om.trace.ContextTracer; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EPackage; @@ -101,6 +102,8 @@ import java.util.Set; */ public class TransactionCommitContext implements InternalCommitContext { + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG_TRANSACTION, TransactionCommitContext.class); + private static final InternalCDORevision DETACHED = new StubCDORevision(null); private final InternalTransaction transaction; @@ -664,7 +667,11 @@ public class TransactionCommitContext implements InternalCommitContext { try { - OM.LOG.error(ex); + if (TRACER.isEnabled()) + { + TRACER.trace(ex); + } + String storeClass = repository.getStore().getClass().getSimpleName(); rollback("Rollback in " + storeClass + ": " + StringUtil.formatException(ex)); //$NON-NLS-1$ //$NON-NLS-2$ } @@ -677,7 +684,10 @@ public class TransactionCommitContext implements InternalCommitContext try { - OM.LOG.error(ex1); + if (TRACER.isEnabled()) + { + TRACER.trace(ex1); + } } catch (Exception ignore) { @@ -741,10 +751,14 @@ public class TransactionCommitContext implements InternalCommitContext try { - InternalSession sender = transaction.getSession(); - CDOCommitInfo commitInfo = success ? createCommitInfo() : createFailureCommitInfo(); + // Send notifications (in particular FailureCommitInfos) only if timeStamp had been allocated + if (timeStamp != CDOBranchPoint.UNSPECIFIED_DATE) + { + InternalSession sender = transaction.getSession(); + CDOCommitInfo commitInfo = success ? createCommitInfo() : createFailureCommitInfo(); - repository.sendCommitNotification(sender, commitInfo, clearResourcePathCache); + repository.sendCommitNotification(sender, commitInfo, clearResourcePathCache); + } } catch (Exception ex) { @@ -1456,7 +1470,7 @@ public class TransactionCommitContext implements InternalCommitContext /** * @author Eike Stepper */ - private static final class RollbackException extends RuntimeException + protected static final class RollbackException extends RuntimeException { private static final long serialVersionUID = 1L; diff --git a/plugins/org.eclipse.emf.cdo.tests.db/src/org/eclipse/emf/cdo/tests/db/H2Config.java b/plugins/org.eclipse.emf.cdo.tests.db/src/org/eclipse/emf/cdo/tests/db/H2Config.java index 46ed8313f2..cf30e97a63 100644 --- a/plugins/org.eclipse.emf.cdo.tests.db/src/org/eclipse/emf/cdo/tests/db/H2Config.java +++ b/plugins/org.eclipse.emf.cdo.tests.db/src/org/eclipse/emf/cdo/tests/db/H2Config.java @@ -65,13 +65,13 @@ public class H2Config extends DBConfig if (defaultDataSource == null) { defaultDataSource = new JdbcDataSource(); - defaultDataSource.setURL("jdbc:h2:" + reusableFolder.getAbsolutePath() + "/h2test"); + defaultDataSource.setURL("jdbc:h2:" + reusableFolder.getAbsolutePath() + "/h2test;LOCK_TIMEOUT=10000"); } H2Adapter.createSchema(defaultDataSource, repoName, !isRestarting()); JdbcDataSource dataSource = new JdbcDataSource(); - dataSource.setURL("jdbc:h2:" + reusableFolder.getAbsolutePath() + "/h2test;SCHEMA=" + repoName); + dataSource.setURL("jdbc:h2:" + reusableFolder.getAbsolutePath() + "/h2test;LOCK_TIMEOUT=10000;SCHEMA=" + repoName); return dataSource; } diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_349804_Test.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_349804_Test.java index a0f8caed41..554db44b1e 100644 --- a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_349804_Test.java +++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_349804_Test.java @@ -4,7 +4,7 @@ * 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: * Eike Stepper - initial API and implementation */ @@ -17,6 +17,7 @@ import org.eclipse.emf.cdo.server.IStoreAccessor.CommitContext; import org.eclipse.emf.cdo.server.ITransaction; import org.eclipse.emf.cdo.session.CDOSession; import org.eclipse.emf.cdo.session.CDOSessionInvalidationEvent; +import org.eclipse.emf.cdo.spi.server.InternalRepository; import org.eclipse.emf.cdo.tests.AbstractCDOTest; import org.eclipse.emf.cdo.tests.model1.Company; import org.eclipse.emf.cdo.transaction.CDOTransaction; @@ -37,33 +38,43 @@ public class Bugzilla_349804_Test extends AbstractCDOTest { public void testInvalidation() throws CommitException, InterruptedException { + long timeStamp; + { + InternalRepository repository = getRepository(); + CDOSession session = openSession(); - CDOTransaction transaction1 = session.openTransaction(); + CDOTransaction transaction = session.openTransaction(); - transaction1.createResource(getResourcePath("test")); - transaction1.commit(); + transaction.createResource(getResourcePath("test")); + timeStamp = transaction.commit().getTimeStamp(); Failure handler = new Failure(); - getRepository().addHandler(handler); - CDOTransaction failureTransaction = session.openTransaction(); - failureTransaction.createResource(getResourcePath("fail")); + repository.addHandler(handler); try { // Creating failure commit. It will change last update time on server TimeStampAuthority - failureTransaction.commit(); + transaction.createResource(getResourcePath("fail")); + transaction.commit(); fail("CommitException expected"); } catch (CommitException expected) { + // Ignore + } + finally + { + repository.removeHandler(handler); + session.close(); } - getRepository().removeHandler(handler); - session.close(); + assertEquals(timeStamp, repository.getLastCommitTimeStamp()); } CDOSession session = openSession(); + assertEquals(timeStamp, session.getLastUpdateTime()); + CDOTransaction transaction = session.openTransaction(); final CountDownLatch invalidationLatch = new CountDownLatch(1); @@ -85,11 +96,13 @@ public class Bugzilla_349804_Test extends AbstractCDOTest // Invalidation shall fail, because it will use lastUpdateTime from TimeStampAuthority for commit result transaction.commit(); - invalidationLatch.await(500, TimeUnit.MILLISECONDS); + invalidationLatch.await(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS); assertEquals("Invalidation was not delivered", 0, invalidationLatch.getCount()); } - public void testDeadlockWithLocking() throws CommitException, InterruptedException, TimeoutException + private int xxx; + + public void _testDeadlockWithLocking() throws CommitException, InterruptedException, TimeoutException { { CDOSession session = openSession(); diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_999999_Test.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_999999_Test.java new file mode 100644 index 0000000000..3014848555 --- /dev/null +++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_999999_Test.java @@ -0,0 +1,256 @@ +/* + * Copyright (c) 2013 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: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.tests.bugzilla; + +import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants; +import org.eclipse.emf.cdo.eresource.CDOResource; +import org.eclipse.emf.cdo.internal.server.Repository; +import org.eclipse.emf.cdo.internal.server.TransactionCommitContext; +import org.eclipse.emf.cdo.session.CDOSession; +import org.eclipse.emf.cdo.spi.server.InternalCommitContext; +import org.eclipse.emf.cdo.spi.server.InternalRepository; +import org.eclipse.emf.cdo.spi.server.InternalTransaction; +import org.eclipse.emf.cdo.tests.AbstractCDOTest; +import org.eclipse.emf.cdo.tests.config.IRepositoryConfig; +import org.eclipse.emf.cdo.tests.config.impl.ConfigTest.CleanRepositoriesAfter; +import org.eclipse.emf.cdo.tests.config.impl.ConfigTest.CleanRepositoriesBefore; +import org.eclipse.emf.cdo.tests.config.impl.RepositoryConfig; +import org.eclipse.emf.cdo.tests.model1.Category; +import org.eclipse.emf.cdo.tests.model1.Company; +import org.eclipse.emf.cdo.transaction.CDOTransaction; +import org.eclipse.emf.cdo.util.CDOUtil; +import org.eclipse.emf.cdo.util.CommitException; +import org.eclipse.emf.cdo.util.ConcurrentAccessException; + +import java.util.Map; + +/** + * @author Eike Stepper + */ +@CleanRepositoriesBefore +@CleanRepositoriesAfter +public class Bugzilla_999999_Test extends AbstractCDOTest +{ + private static final String RESOURCE_NAME = "res1"; + + private static final int CLIENTS = 20; + + private static final int COMMITS_PER_CLIENT = 3; + + private static final String BAD_COMMIT = "Bad Commit"; + + public void testWithoutFailure() throws Exception + { + executeTestWith(FailureTime.NEVER); + } + + public void testWithFailureBeforeTimeStamp() throws Exception + { + executeTestWith(FailureTime.BEFORE_TIMESTAMP); + } + + public void testWithFailureAfterTimeStamp() throws Exception + { + executeTestWith(FailureTime.AFTER_TIMESTAMP); + } + + private void initRepository(final FailureTime failureTime) + { + Repository repository = new Repository.Default() + { + @Override + public InternalCommitContext createCommitContext(InternalTransaction transaction) + { + return new TransactionCommitContext(transaction) + { + @Override + protected void lockObjects() throws InterruptedException + { + if (failureTime == FailureTime.BEFORE_TIMESTAMP) + { + simulateProblem(); + } + + super.lockObjects(); + } + + @Override + protected void adjustForCommit() + { + if (failureTime == FailureTime.AFTER_TIMESTAMP) + { + simulateProblem(); + } + + super.adjustForCommit(); + } + + private void simulateProblem() + { + if (BAD_COMMIT.equals(getCommitComment())) + { + RuntimeException cause = new RuntimeException("SIMULATED PROBLEM"); + throw new RollbackException(CDOProtocolConstants.ROLLBACK_REASON_OPTIMISTIC_LOCKING, cause); + } + } + }; + } + }; + + repository.setName(IRepositoryConfig.REPOSITORY_NAME); + + Map<String, String> props = getRepositoryProperties(); + ((InternalRepository)repository).setProperties(props); + + getTestProperties().put(RepositoryConfig.PROP_TEST_REPOSITORY, repository); + } + + private void executeTestWith(FailureTime failureTime) throws ConcurrentAccessException, CommitException, + InterruptedException + { + disableConsole(); + initRepository(failureTime); + + CDOSession session = openSession(); + CDOTransaction transaction = session.openTransaction(); + CDOResource resource = transaction.createResource(getResourcePath(RESOURCE_NAME)); + resource.getContents().add(getModel1Factory().createCompany()); + transaction.commit(); + + Thread[] clients = new Thread[CLIENTS]; + for (int id = 0; id < CLIENTS; id++) + { + if (failureTime == FailureTime.NEVER || id < CLIENTS - 1) + { + clients[id] = new GoodClient(id); + } + else + { + clients[id] = new BadClient(id); + } + } + + for (int id = 0; id < CLIENTS; id++) + { + clients[id].start(); + } + + for (int id = 0; id < CLIENTS; id++) + { + clients[id].join(); + } + + assertEquals("Invalidation queue is blocked", true, + session.waitForUpdate(getRepository().getLastCommitTimeStamp(), DEFAULT_TIMEOUT)); + } + + /** + * @author Eike Stepper + */ + private enum FailureTime + { + NEVER, BEFORE_TIMESTAMP, AFTER_TIMESTAMP + } + + /** + * @author Eike Stepper + */ + private final class GoodClient extends Thread + { + private final int id; + + private Category category; + + public GoodClient(int id) throws ConcurrentAccessException, CommitException + { + super("GoodClient-" + id); + this.id = id; + setDaemon(true); + + category = getModel1Factory().createCategory(); + category.setName(GoodClient.this.getName()); + + CDOSession session = openSession(); + CDOTransaction transaction = session.openTransaction(); + CDOResource resource = transaction.getResource(getResourcePath(RESOURCE_NAME)); + Company company = (Company)resource.getContents().get(0); + + company.getCategories().add(category); + transaction.commit(); + } + + @Override + public void run() + { + CDOTransaction transaction = (CDOTransaction)CDOUtil.getCDOObject(category).cdoView(); + int objectsPerCommit = 10 * (CLIENTS - id); + + for (int commit = 0; commit < COMMITS_PER_CLIENT; commit++) + { + for (int object = 0; object < objectsPerCommit; object++) + { + category.getProducts().add(getModel1Factory().createProduct1()); + } + + try + { + transaction.commit(); + } + catch (CommitException ex) + { + ex.printStackTrace(); + break; + } + } + } + } + + /** + * @author Eike Stepper + */ + private final class BadClient extends Thread + { + private Company company; + + public BadClient(int id) throws ConcurrentAccessException, CommitException + { + super("BadClient-" + id); + setDaemon(true); + + CDOSession session = openSession(); + CDOTransaction transaction = session.openTransaction(); + CDOResource resource = transaction.getResource(getResourcePath(RESOURCE_NAME)); + company = (Company)resource.getContents().get(0); + } + + @Override + public void run() + { + CDOTransaction transaction = (CDOTransaction)CDOUtil.getCDOObject(company).cdoView(); + for (int commit = 0; commit < 100 * COMMITS_PER_CLIENT; commit++) + { + company.setName("Company-" + commit); // Make the transaction dirty + + try + { + transaction.setCommitComment(BAD_COMMIT); + transaction.commit(); + } + catch (CommitException ex) + { + transaction.rollback(); + } + + // Bugzilla_999999_Test.sleep(10); + } + } + } +} diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/session/CDOSessionImpl.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/session/CDOSessionImpl.java index b783e08b74..ad32a8060c 100644 --- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/session/CDOSessionImpl.java +++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/session/CDOSessionImpl.java @@ -1056,6 +1056,24 @@ public abstract class CDOSessionImpl extends CDOTransactionContainerImpl impleme long previousTimeStamp = commitInfo.getPreviousTimeStamp(); long lastUpdateTime = getLastUpdateTime(); + int xxx; + if (sessionID == 3) + { + ArrayList<Long> queue; + synchronized (outOfSequenceInvalidations) + { + queue = new ArrayList<Long>(outOfSequenceInvalidations.size()); + for (OutOfSequenceInvalidation invalidation : outOfSequenceInvalidations.values()) + { + queue.add(invalidation.getCommitInfo().getTimeStamp() % 10000L); + } + } + + Collections.sort(queue); + System.out.println("(" + lastUpdateTime % 10000L + ") " + commitInfo.getClass().getSimpleName() + ": " + + previousTimeStamp % 10000L + " --> " + commitInfo.getTimeStamp() % 10000L + " Queue=" + queue); + } + if (previousTimeStamp < lastUpdateTime) { previousTimeStamp = lastUpdateTime; @@ -1142,33 +1160,43 @@ public abstract class CDOSessionImpl extends CDOTransactionContainerImpl impleme private void invalidateOrdered(CDOCommitInfo commitInfo, InternalCDOTransaction sender, boolean clearResourcePathCache) { - Map<CDOID, InternalCDORevision> oldRevisions = null; - boolean success = commitInfo.getBranch() != null; - if (success) + try { - oldRevisions = reviseRevisions(commitInfo); - } + Map<CDOID, InternalCDORevision> oldRevisions = null; + boolean success = commitInfo.getBranch() != null; + if (success) + { + oldRevisions = reviseRevisions(commitInfo); + } - if (options.isPassiveUpdateEnabled()) - { - setLastUpdateTime(commitInfo.getTimeStamp()); - } + if (options.isPassiveUpdateEnabled()) + { + setLastUpdateTime(commitInfo.getTimeStamp()); + } - if (success) - { - fireInvalidationEvent(sender, commitInfo); - commitInfoManager.notifyCommitInfoHandlers(commitInfo); - } + if (success) + { + fireInvalidationEvent(sender, commitInfo); + commitInfoManager.notifyCommitInfoHandlers(commitInfo); + } - for (InternalCDOView view : getViews()) - { - if (view != sender) + for (InternalCDOView view : getViews()) { - invalidateView(commitInfo, view, oldRevisions, clearResourcePathCache); + if (view != sender) + { + invalidateView(commitInfo, view, oldRevisions, clearResourcePathCache); + } + else + { + view.setLastUpdateTime(commitInfo.getTimeStamp()); + } } - else + } + catch (RuntimeException ex) + { + if (isActive()) { - view.setLastUpdateTime(commitInfo.getTimeStamp()); + throw ex; } } } diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/transaction/CDOTransactionImpl.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/transaction/CDOTransactionImpl.java index 21d9f1a5f2..059ccd7e46 100644 --- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/transaction/CDOTransactionImpl.java +++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/transaction/CDOTransactionImpl.java @@ -2932,11 +2932,12 @@ public class CDOTransactionImpl extends CDOViewImpl implements InternalCDOTransa { InternalCDOSession session = getSession(); long timeStamp = result.getTimeStamp(); + boolean clearResourcePathCache = result.isClearResourcePathCache(); if (result.getRollbackMessage() != null) { CDOCommitInfo commitInfo = new FailureCommitInfo(timeStamp, result.getPreviousTimeStamp()); - session.invalidate(commitInfo, transaction, result.isClearResourcePathCache()); + session.invalidate(commitInfo, transaction, clearResourcePathCache); return; } @@ -2969,7 +2970,6 @@ public class CDOTransactionImpl extends CDOViewImpl implements InternalCDOTransa CDOCommitInfo commitInfo = makeCommitInfo(timeStamp, result.getPreviousTimeStamp()); if (!commitInfo.isEmpty()) { - boolean clearResourcePathCache = result.isClearResourcePathCache(); session.invalidate(commitInfo, transaction, clearResourcePathCache); } diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/view/CDOViewImpl.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/view/CDOViewImpl.java index dc2a1693ac..0bafbc8e1c 100644 --- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/view/CDOViewImpl.java +++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/view/CDOViewImpl.java @@ -859,7 +859,7 @@ public class CDOViewImpl extends AbstractCDOView { try { - if (branch == getBranch()) + if (branch == getBranch()) // Also false for FailureCommitInfos. Only setLastUpdateTime() is called below. { if (clearResourcePathCache) { |