diff --git a/lib-commonbase/source/java/ch/ethz/sis/transaction/AbstractTransactionNode.java b/lib-commonbase/source/java/ch/ethz/sis/transaction/AbstractTransactionNode.java index 87c7a9df8de7be98e92099a9047db40117316009..01159d3aef8529487c5a5f3348e290d0e09d3a14 100644 --- a/lib-commonbase/source/java/ch/ethz/sis/transaction/AbstractTransactionNode.java +++ b/lib-commonbase/source/java/ch/ethz/sis/transaction/AbstractTransactionNode.java @@ -118,7 +118,7 @@ public abstract class AbstractTransactionNode<T extends AbstractTransaction> registerTransaction(transaction); operationLog.info( "Recovered transaction '" + transaction.getTransactionId() + "' found in the transaction log with last status '" - + transaction.getTransactionStatus() + "' ."); + + transaction.getTransactionStatus() + "'."); } } } diff --git a/test-integration/sourceTest/java/ch/ethz/sis/openbis/systemtests/Integration2PCTest.java b/test-integration/sourceTest/java/ch/ethz/sis/openbis/systemtests/Integration2PCTest.java index a0f6146a30f96ffa4f2ad25501845c6b0c7ab4f3..a0b8f4d9f8a33ec76278e297f4af0b2ff5166019 100644 --- a/test-integration/sourceTest/java/ch/ethz/sis/openbis/systemtests/Integration2PCTest.java +++ b/test-integration/sourceTest/java/ch/ethz/sis/openbis/systemtests/Integration2PCTest.java @@ -21,7 +21,6 @@ import java.util.UUID; import javax.sql.DataSource; import org.testng.annotations.AfterMethod; -import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import ch.ethz.sis.openbis.generic.OpenBIS; @@ -34,6 +33,7 @@ import ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment.id.ExperimentPermId; import ch.ethz.sis.openbis.generic.asapi.v3.dto.project.Project; import ch.ethz.sis.openbis.generic.asapi.v3.dto.project.create.ProjectCreation; import ch.ethz.sis.openbis.generic.asapi.v3.dto.project.fetchoptions.ProjectFetchOptions; +import ch.ethz.sis.openbis.generic.asapi.v3.dto.project.id.ProjectIdentifier; import ch.ethz.sis.openbis.generic.asapi.v3.dto.project.id.ProjectPermId; import ch.ethz.sis.openbis.generic.asapi.v3.dto.space.Space; import ch.ethz.sis.openbis.generic.asapi.v3.dto.space.create.SpaceCreation; @@ -47,22 +47,9 @@ import ch.systemsx.cisd.common.test.AssertionUtil; public class Integration2PCTest extends AbstractIntegrationTest { - private static final String CODE_PREFIX = "TRANSACTION_TEST_"; + private static final String ENTITY_CODE_PREFIX = "TRANSACTION_TEST_"; - private static final String OWNER_PREFIX = "test-owner-"; - - private static final String SOURCE_PREFIX = "test-source-"; - - private static final String CONTENT = "test-content"; - - private TransactionCoordinatorApi coordinatorApi; - - @BeforeMethod - public void beforeMethod(Method method) - { - super.beforeMethod(method); - coordinatorApi = (TransactionCoordinatorApi) applicationServerSpringContext.getBean(ITransactionCoordinatorApi.class); - } + private static final long WAITING_TIME_FOR_FINISHING_TRANSACTIONS = 2000L; @AfterMethod public void afterMethod(Method method) throws Exception @@ -96,26 +83,16 @@ public class Integration2PCTest extends AbstractIntegrationTest UUID transactionId = openBISWithTr.beginTransaction(); - String owner = OWNER_PREFIX + UUID.randomUUID(); - String source = SOURCE_PREFIX + UUID.randomUUID(); - byte[] bytesToWrite = CONTENT.getBytes(StandardCharsets.UTF_8); - - openBISWithTr.getAfsServerFacade().write(owner, source, 0L, bytesToWrite, calculateMD5(bytesToWrite)); - - SpaceCreation spaceCreation = new SpaceCreation(); - spaceCreation.setCode(CODE_PREFIX + UUID.randomUUID()); + WriteData writeData = createWriteData(); + openBISWithTr.getAfsServerFacade().write(writeData.owner, writeData.source, 0L, writeData.bytes, writeData.md5); + SpaceCreation spaceCreation = createSpaceCreation(); SpacePermId spaceId = openBISWithTr.createSpaces(List.of(spaceCreation)).get(0); - ProjectCreation projectCreation = new ProjectCreation(); - projectCreation.setSpaceId(spaceId); - projectCreation.setCode(CODE_PREFIX + UUID.randomUUID()); - + ProjectCreation projectCreation = createProjectCreation(spaceCreation.getCode()); ProjectPermId projectId = openBISWithTr.createProjects(List.of(projectCreation)).get(0); - ExperimentCreation experimentCreation = new ExperimentCreation(); - experimentCreation.setProjectId(projectId); - experimentCreation.setCode(CODE_PREFIX + UUID.randomUUID()); + ExperimentCreation experimentCreation = createExperimentCreation(spaceCreation.getCode(), projectCreation.getCode(), null); try { @@ -127,10 +104,7 @@ public class Integration2PCTest extends AbstractIntegrationTest AssertionUtil.assertContains("Type id cannot be null", e.getCause().getMessage()); } - ExperimentCreation experimentCreation2 = new ExperimentCreation(); - experimentCreation2.setTypeId(new EntityTypePermId("UNKNOWN")); - experimentCreation2.setProjectId(projectId); - experimentCreation2.setCode(CODE_PREFIX + UUID.randomUUID()); + ExperimentCreation experimentCreation2 = createExperimentCreation(spaceCreation.getCode(), projectCreation.getCode(), "UNKNOWN"); ExperimentPermId experimentId = openBISWithTr.createExperiments(List.of(experimentCreation2)).get(0); @@ -154,7 +128,7 @@ public class Integration2PCTest extends AbstractIntegrationTest assertNull(noTrProjectBefore); assertNull(noTrExperimentBefore); - assertTransactions(coordinatorApi.getTransactionMap(), new TestTransaction(transactionId, TransactionStatus.BEGIN_FINISHED)); + assertTransactions(getTransactionCoordinator().getTransactionMap(), new TestTransaction(transactionId, TransactionStatus.BEGIN_FINISHED)); if (rollback) { @@ -164,7 +138,7 @@ public class Integration2PCTest extends AbstractIntegrationTest openBISWithTr.commitTransaction(); } - assertTransactions(coordinatorApi.getTransactionMap()); + assertTransactions(getTransactionCoordinator().getTransactionMap()); Space trSpaceAfter = openBISWithTr.getSpaces(Collections.singletonList(spaceId), new SpaceFetchOptions()).get(spaceId); Project trProjectAfter = openBISWithTr.getProjects(Collections.singletonList(projectId), new ProjectFetchOptions()).get(projectId); @@ -197,11 +171,11 @@ public class Integration2PCTest extends AbstractIntegrationTest assertNotNull(noTrProjectAfter); assertNotNull(noTrExperimentAfter); - byte[] trBytesRead = openBISWithTr.getAfsServerFacade().read(owner, source, 0L, bytesToWrite.length); - byte[] noTrBytesRead = openBISWithNoTr.getAfsServerFacade().read(owner, source, 0L, bytesToWrite.length); + byte[] trBytesRead = openBISWithTr.getAfsServerFacade().read(writeData.owner, writeData.source, 0L, writeData.bytes.length); + byte[] noTrBytesRead = openBISWithNoTr.getAfsServerFacade().read(writeData.owner, writeData.source, 0L, writeData.bytes.length); - assertEquals(new String(trBytesRead, StandardCharsets.UTF_8), CONTENT); - assertEquals(new String(noTrBytesRead, StandardCharsets.UTF_8), CONTENT); + assertEquals(new String(trBytesRead, StandardCharsets.UTF_8), writeData.content); + assertEquals(new String(noTrBytesRead, StandardCharsets.UTF_8), writeData.content); } } @@ -226,15 +200,12 @@ public class Integration2PCTest extends AbstractIntegrationTest UUID transactionId = openBIS.beginTransaction(); - String owner = OWNER_PREFIX + UUID.randomUUID(); - String source = SOURCE_PREFIX + UUID.randomUUID(); - byte[] bytesToWrite = CONTENT.getBytes(StandardCharsets.UTF_8); - openBIS.getAfsServerFacade().write(owner, source, 0L, bytesToWrite, calculateMD5(bytesToWrite)); + WriteData writeData = createWriteData(); + openBIS.getAfsServerFacade().write(writeData.owner, writeData.source, 0L, writeData.bytes, writeData.md5); - assertTransactions(coordinatorApi.getTransactionMap(), new TestTransaction(transactionId, TransactionStatus.BEGIN_FINISHED)); + assertTransactions(getTransactionCoordinator().getTransactionMap(), new TestTransaction(transactionId, TransactionStatus.BEGIN_FINISHED)); - SpaceCreation spaceCreation = new SpaceCreation(); - spaceCreation.setCode(CODE_PREFIX + UUID.randomUUID()); + SpaceCreation spaceCreation = createSpaceCreation(); try { @@ -249,7 +220,7 @@ public class Integration2PCTest extends AbstractIntegrationTest "Begin transaction '" + transactionId + "' failed for participant 'application-server'."); } - assertTransactions(coordinatorApi.getTransactionMap(), new TestTransaction(transactionId, TransactionStatus.BEGIN_FINISHED)); + assertTransactions(getTransactionCoordinator().getTransactionMap(), new TestTransaction(transactionId, TransactionStatus.BEGIN_FINISHED)); // make begin succeed at AS setApplicationServerProxyInterceptor((method, defaultAction) -> defaultAction.call()); @@ -259,7 +230,7 @@ public class Integration2PCTest extends AbstractIntegrationTest openBIS.commitTransaction(); - assertTransactions(coordinatorApi.getTransactionMap()); + assertTransactions(getTransactionCoordinator().getTransactionMap()); // check committed data OpenBIS openBISNoTr = createOpenBIS(); @@ -268,8 +239,8 @@ public class Integration2PCTest extends AbstractIntegrationTest Space createdSpace = openBISNoTr.getSpaces(List.of(spaceId), new SpaceFetchOptions()).get(spaceId); assertNotNull(createdSpace); - byte[] bytesRead = openBISNoTr.getAfsServerFacade().read(owner, source, 0L, bytesToWrite.length); - assertEquals(new String(bytesRead, StandardCharsets.UTF_8), CONTENT); + byte[] bytesRead = openBISNoTr.getAfsServerFacade().read(writeData.owner, writeData.source, 0L, writeData.bytes.length); + assertEquals(new String(bytesRead, StandardCharsets.UTF_8), writeData.content); } @Test @@ -293,21 +264,16 @@ public class Integration2PCTest extends AbstractIntegrationTest UUID transactionId = openBIS.beginTransaction(); - SpaceCreation spaceCreation = new SpaceCreation(); - spaceCreation.setCode(CODE_PREFIX + UUID.randomUUID()); - + SpaceCreation spaceCreation = createSpaceCreation(); SpacePermId spaceId = openBIS.createSpaces(List.of(spaceCreation)).get(0); - String owner = OWNER_PREFIX + UUID.randomUUID(); - String source = SOURCE_PREFIX + UUID.randomUUID(); - byte[] bytesToWrite = CONTENT.getBytes(StandardCharsets.UTF_8); - - assertTransactions(coordinatorApi.getTransactionMap(), new TestTransaction(transactionId, TransactionStatus.BEGIN_FINISHED)); + WriteData writeData = createWriteData(); + assertTransactions(getTransactionCoordinator().getTransactionMap(), new TestTransaction(transactionId, TransactionStatus.BEGIN_FINISHED)); try { // first attempt - openBIS.getAfsServerFacade().write(owner, source, 0L, bytesToWrite, calculateMD5(bytesToWrite)); + openBIS.getAfsServerFacade().write(writeData.owner, writeData.source, 0L, writeData.bytes, writeData.md5); fail(); } catch (Exception e) { @@ -316,17 +282,17 @@ public class Integration2PCTest extends AbstractIntegrationTest assertEquals(e.getCause().getMessage(), "Begin transaction '" + transactionId + "' failed for participant 'afs-server'."); } - assertTransactions(coordinatorApi.getTransactionMap(), new TestTransaction(transactionId, TransactionStatus.BEGIN_FINISHED)); + assertTransactions(getTransactionCoordinator().getTransactionMap(), new TestTransaction(transactionId, TransactionStatus.BEGIN_FINISHED)); // make begin succeed at AFS setAfsServerProxyInterceptor((method, defaultAction) -> defaultAction.call()); // second attempt - openBIS.getAfsServerFacade().write(owner, source, 0L, bytesToWrite, calculateMD5(bytesToWrite)); + openBIS.getAfsServerFacade().write(writeData.owner, writeData.source, 0L, writeData.bytes, writeData.md5); openBIS.commitTransaction(); - assertTransactions(coordinatorApi.getTransactionMap()); + assertTransactions(getTransactionCoordinator().getTransactionMap()); // check committed data OpenBIS openBISNoTr = createOpenBIS(); @@ -335,8 +301,8 @@ public class Integration2PCTest extends AbstractIntegrationTest Space createdSpace = openBISNoTr.getSpaces(List.of(spaceId), new SpaceFetchOptions()).get(spaceId); assertNotNull(createdSpace); - byte[] bytesRead = openBISNoTr.getAfsServerFacade().read(owner, source, 0L, bytesToWrite.length); - assertEquals(new String(bytesRead, StandardCharsets.UTF_8), CONTENT); + byte[] bytesRead = openBISNoTr.getAfsServerFacade().read(writeData.owner, writeData.source, 0L, writeData.bytes.length); + assertEquals(new String(bytesRead, StandardCharsets.UTF_8), writeData.content); } @Test @@ -360,15 +326,12 @@ public class Integration2PCTest extends AbstractIntegrationTest UUID transactionId = openBIS.beginTransaction(); - String owner = OWNER_PREFIX + UUID.randomUUID(); - String source = SOURCE_PREFIX + UUID.randomUUID(); - byte[] bytesToWrite = CONTENT.getBytes(StandardCharsets.UTF_8); - openBIS.getAfsServerFacade().write(owner, source, 0L, bytesToWrite, calculateMD5(bytesToWrite)); + WriteData writeData = createWriteData(); + openBIS.getAfsServerFacade().write(writeData.owner, writeData.source, 0L, writeData.bytes, writeData.md5); - assertTransactions(coordinatorApi.getTransactionMap(), new TestTransaction(transactionId, TransactionStatus.BEGIN_FINISHED)); + assertTransactions(getTransactionCoordinator().getTransactionMap(), new TestTransaction(transactionId, TransactionStatus.BEGIN_FINISHED)); - SpaceCreation spaceCreation = new SpaceCreation(); - spaceCreation.setCode(CODE_PREFIX + UUID.randomUUID()); + SpaceCreation spaceCreation = createSpaceCreation(); SpacePermId spaceId = openBIS.createSpaces(List.of(spaceCreation)).get(0); try @@ -384,7 +347,7 @@ public class Integration2PCTest extends AbstractIntegrationTest + "' failed for participant 'application-server'. The transaction was rolled back."); } - assertTransactions(coordinatorApi.getTransactionMap()); + assertTransactions(getTransactionCoordinator().getTransactionMap()); // check data hasn't been committed OpenBIS openBISNoTr = createOpenBIS(); @@ -395,7 +358,7 @@ public class Integration2PCTest extends AbstractIntegrationTest try { - openBISNoTr.getAfsServerFacade().read(owner, source, 0L, bytesToWrite.length); + openBISNoTr.getAfsServerFacade().read(writeData.owner, writeData.source, 0L, writeData.bytes.length); fail(); } catch (Exception expected) { @@ -423,15 +386,12 @@ public class Integration2PCTest extends AbstractIntegrationTest UUID transactionId = openBIS.beginTransaction(); - String owner = OWNER_PREFIX + UUID.randomUUID(); - String source = SOURCE_PREFIX + UUID.randomUUID(); - byte[] bytesToWrite = CONTENT.getBytes(StandardCharsets.UTF_8); - openBIS.getAfsServerFacade().write(owner, source, 0L, bytesToWrite, calculateMD5(bytesToWrite)); + WriteData writeData = createWriteData(); + openBIS.getAfsServerFacade().write(writeData.owner, writeData.source, 0L, writeData.bytes, writeData.md5); - assertTransactions(coordinatorApi.getTransactionMap(), new TestTransaction(transactionId, TransactionStatus.BEGIN_FINISHED)); + assertTransactions(getTransactionCoordinator().getTransactionMap(), new TestTransaction(transactionId, TransactionStatus.BEGIN_FINISHED)); - SpaceCreation spaceCreation = new SpaceCreation(); - spaceCreation.setCode(CODE_PREFIX + UUID.randomUUID()); + SpaceCreation spaceCreation = createSpaceCreation(); SpacePermId spaceId = openBIS.createSpaces(List.of(spaceCreation)).get(0); try @@ -447,7 +407,7 @@ public class Integration2PCTest extends AbstractIntegrationTest + "' failed for participant 'afs-server'. The transaction was rolled back."); } - assertTransactions(coordinatorApi.getTransactionMap()); + assertTransactions(getTransactionCoordinator().getTransactionMap()); // check data hasn't been committed OpenBIS openBISNoTr = createOpenBIS(); @@ -458,7 +418,7 @@ public class Integration2PCTest extends AbstractIntegrationTest try { - openBISNoTr.getAfsServerFacade().read(owner, source, 0L, bytesToWrite.length); + openBISNoTr.getAfsServerFacade().read(writeData.owner, writeData.source, 0L, writeData.bytes.length); fail(); } catch (Exception expected) { @@ -486,29 +446,26 @@ public class Integration2PCTest extends AbstractIntegrationTest UUID transactionId = openBIS.beginTransaction(); - String owner = OWNER_PREFIX + UUID.randomUUID(); - String source = SOURCE_PREFIX + UUID.randomUUID(); - byte[] bytesToWrite = CONTENT.getBytes(StandardCharsets.UTF_8); - openBIS.getAfsServerFacade().write(owner, source, 0L, bytesToWrite, calculateMD5(bytesToWrite)); + WriteData writeData = createWriteData(); + openBIS.getAfsServerFacade().write(writeData.owner, writeData.source, 0L, writeData.bytes, writeData.md5); - assertTransactions(coordinatorApi.getTransactionMap(), new TestTransaction(transactionId, TransactionStatus.BEGIN_FINISHED)); + assertTransactions(getTransactionCoordinator().getTransactionMap(), new TestTransaction(transactionId, TransactionStatus.BEGIN_FINISHED)); - SpaceCreation spaceCreation = new SpaceCreation(); - spaceCreation.setCode(CODE_PREFIX + UUID.randomUUID()); + SpaceCreation spaceCreation = createSpaceCreation(); SpacePermId spaceId = openBIS.createSpaces(List.of(spaceCreation)).get(0); // commit (no exception is thrown because prepare succeeded at both AS and AFS, the failed commit at AS will be internally retried) openBIS.commitTransaction(); - assertTransactions(coordinatorApi.getTransactionMap(), new TestTransaction(transactionId, TransactionStatus.COMMIT_STARTED)); + assertTransactions(getTransactionCoordinator().getTransactionMap(), new TestTransaction(transactionId, TransactionStatus.COMMIT_STARTED)); // make commit succeed at AS setApplicationServerProxyInterceptor((method, defaultAction) -> defaultAction.call()); - // finishing of failed transactions is configured to run every second - let's wait 2 seconds to give it enough time to redo commit - Thread.sleep(2000); + // let's wait for the task that tries to finish failed or abandoned transactions runs + Thread.sleep(WAITING_TIME_FOR_FINISHING_TRANSACTIONS); - assertTransactions(coordinatorApi.getTransactionMap()); + assertTransactions(getTransactionCoordinator().getTransactionMap()); // check committed data OpenBIS openBISNoTr = createOpenBIS(); @@ -517,8 +474,8 @@ public class Integration2PCTest extends AbstractIntegrationTest Space createdSpace = openBISNoTr.getSpaces(List.of(spaceId), new SpaceFetchOptions()).get(spaceId); assertNotNull(createdSpace); - byte[] bytesRead = openBISNoTr.getAfsServerFacade().read(owner, source, 0L, bytesToWrite.length); - assertEquals(new String(bytesRead, StandardCharsets.UTF_8), CONTENT); + byte[] bytesRead = openBISNoTr.getAfsServerFacade().read(writeData.owner, writeData.source, 0L, writeData.bytes.length); + assertEquals(new String(bytesRead, StandardCharsets.UTF_8), writeData.content); } @Test @@ -542,29 +499,26 @@ public class Integration2PCTest extends AbstractIntegrationTest UUID transactionId = openBIS.beginTransaction(); - String owner = OWNER_PREFIX + UUID.randomUUID(); - String source = SOURCE_PREFIX + UUID.randomUUID(); - byte[] bytesToWrite = CONTENT.getBytes(StandardCharsets.UTF_8); - openBIS.getAfsServerFacade().write(owner, source, 0L, bytesToWrite, calculateMD5(bytesToWrite)); + WriteData writeData = createWriteData(); + openBIS.getAfsServerFacade().write(writeData.owner, writeData.source, 0L, writeData.bytes, writeData.md5); - assertTransactions(coordinatorApi.getTransactionMap(), new TestTransaction(transactionId, TransactionStatus.BEGIN_FINISHED)); + assertTransactions(getTransactionCoordinator().getTransactionMap(), new TestTransaction(transactionId, TransactionStatus.BEGIN_FINISHED)); - SpaceCreation spaceCreation = new SpaceCreation(); - spaceCreation.setCode(CODE_PREFIX + UUID.randomUUID()); + SpaceCreation spaceCreation = createSpaceCreation(); SpacePermId spaceId = openBIS.createSpaces(List.of(spaceCreation)).get(0); // commit (no exception is thrown because prepare succeeded at both AS and AFS, the failed commit at AFS will be internally retried) openBIS.commitTransaction(); - assertTransactions(coordinatorApi.getTransactionMap(), new TestTransaction(transactionId, TransactionStatus.COMMIT_STARTED)); + assertTransactions(getTransactionCoordinator().getTransactionMap(), new TestTransaction(transactionId, TransactionStatus.COMMIT_STARTED)); // make commit succeed at AFS setAfsServerProxyInterceptor((method, defaultAction) -> defaultAction.call()); - // finishing of failed transactions is configured to run every second - let's wait 2 seconds to give it enough time to redo commit - Thread.sleep(2000); + // let's wait for the task that tries to finish failed or abandoned transactions runs + Thread.sleep(WAITING_TIME_FOR_FINISHING_TRANSACTIONS); - assertTransactions(coordinatorApi.getTransactionMap()); + assertTransactions(getTransactionCoordinator().getTransactionMap()); // check committed data OpenBIS openBISNoTr = createOpenBIS(); @@ -573,8 +527,133 @@ public class Integration2PCTest extends AbstractIntegrationTest Space createdSpace = openBISNoTr.getSpaces(List.of(spaceId), new SpaceFetchOptions()).get(spaceId); assertNotNull(createdSpace); - byte[] bytesRead = openBISNoTr.getAfsServerFacade().read(owner, source, 0L, bytesToWrite.length); - assertEquals(new String(bytesRead, StandardCharsets.UTF_8), CONTENT); + byte[] bytesRead = openBISNoTr.getAfsServerFacade().read(writeData.owner, writeData.source, 0L, writeData.bytes.length); + assertEquals(new String(bytesRead, StandardCharsets.UTF_8), writeData.content); + } + + @Test + public void testRecovery() throws Exception + { + // make commit fail at both AS and AFS (prepare will succeed) + setApplicationServerProxyInterceptor((method, defaultAction) -> + { + if (method != null && method.equals("commitTransaction")) + { + throw new RuntimeException("Test commit exception"); + } else + { + defaultAction.call(); + } + }); + setAfsServerProxyInterceptor((method, defaultAction) -> + { + if (method != null && method.equals("commit")) + { + throw new RuntimeException("Test commit exception"); + } else + { + defaultAction.call(); + } + }); + + // transaction 1 is committed before the crash (prepare is successful but commit fails at both AS and AFS) + OpenBIS openBISWithCommittedTransaction = createOpenBIS(); + openBISWithCommittedTransaction.setInteractiveSessionKey(TEST_INTERACTIVE_SESSION_KEY); + openBISWithCommittedTransaction.login(USER, PASSWORD); + + UUID committedTransactionId = openBISWithCommittedTransaction.beginTransaction(); + + SpaceCreation spaceCreationCommitted = createSpaceCreation(); + SpacePermId spaceIdCommitted = openBISWithCommittedTransaction.createSpaces(List.of(spaceCreationCommitted)).get(0); + + WriteData writeDataCommitted = createWriteData(); + openBISWithCommittedTransaction.getAfsServerFacade() + .write(writeDataCommitted.owner, writeDataCommitted.source, 0L, writeDataCommitted.bytes, writeDataCommitted.md5); + + openBISWithCommittedTransaction.commitTransaction(); + + // transaction 2 is not committed before the crash + OpenBIS openBISWithNotCommittedTransaction = createOpenBIS(); + openBISWithNotCommittedTransaction.setInteractiveSessionKey(TEST_INTERACTIVE_SESSION_KEY); + openBISWithNotCommittedTransaction.login(USER, PASSWORD); + + UUID notCommittedTransactionId = openBISWithNotCommittedTransaction.beginTransaction(); + + SpaceCreation spaceCreationNotCommitted = createSpaceCreation(); + SpacePermId spaceIdNotCommitted = openBISWithNotCommittedTransaction.createSpaces(List.of(spaceCreationNotCommitted)).get(0); + + WriteData writeDataNotCommitted = createWriteData(); + openBISWithNotCommittedTransaction.getAfsServerFacade() + .write(writeDataNotCommitted.owner, writeDataNotCommitted.source, 0L, writeDataNotCommitted.bytes, writeDataNotCommitted.md5); + + // check transactions state before the crash + assertTransactions(getTransactionCoordinator().getTransactionMap(), + new TestTransaction(committedTransactionId, TransactionStatus.COMMIT_STARTED), + new TestTransaction(notCommittedTransactionId, TransactionStatus.BEGIN_FINISHED)); + + OpenBIS openBISNoTr = createOpenBIS(); + openBISNoTr.login(USER, PASSWORD); + + Space committedSpace = openBISNoTr.getSpaces(List.of(spaceIdCommitted), new SpaceFetchOptions()).get(spaceIdCommitted); + assertNull(committedSpace); + + try + { + openBISNoTr.getAfsServerFacade().read(writeDataCommitted.owner, writeDataCommitted.source, 0L, writeDataCommitted.bytes.length); + fail(); + } catch (Exception ignore) + { + } + + Space notCommittedSpace = openBISNoTr.getSpaces(List.of(spaceIdNotCommitted), new SpaceFetchOptions()).get(spaceIdNotCommitted); + assertNull(notCommittedSpace); + + try + { + openBISNoTr.getAfsServerFacade().read(writeDataNotCommitted.owner, writeDataNotCommitted.source, 0L, writeDataNotCommitted.bytes.length); + fail(); + } catch (Exception ignore) + { + } + + // simulate servers crash + restartApplicationServer(); + restartAfsServer(); + + // make commit succeed at both AS and AFS + setApplicationServerProxyInterceptor((method, defaultAction) -> defaultAction.call()); + setAfsServerProxyInterceptor((method, defaultAction) -> defaultAction.call()); + + // let's wait for the task that tries to finish failed or abandoned transactions runs + Thread.sleep(WAITING_TIME_FOR_FINISHING_TRANSACTIONS); + + // check transactions state after the crash + openBISNoTr.login(USER, PASSWORD); + + assertTransactions(getTransactionCoordinator().getTransactionMap()); + + committedSpace = openBISNoTr.getSpaces(List.of(spaceIdCommitted), new SpaceFetchOptions()).get(spaceIdCommitted); + assertNotNull(committedSpace); + + byte[] committedBytesRead = + openBISNoTr.getAfsServerFacade().read(writeDataCommitted.owner, writeDataCommitted.source, 0L, writeDataCommitted.bytes.length); + assertEquals(new String(committedBytesRead, StandardCharsets.UTF_8), writeDataCommitted.content); + + notCommittedSpace = openBISNoTr.getSpaces(List.of(spaceIdNotCommitted), new SpaceFetchOptions()).get(spaceIdNotCommitted); + assertNull(notCommittedSpace); + + try + { + openBISNoTr.getAfsServerFacade().read(writeDataNotCommitted.owner, writeDataNotCommitted.source, 0L, writeDataNotCommitted.bytes.length); + fail(); + } catch (Exception ignore) + { + } + } + + private TransactionCoordinatorApi getTransactionCoordinator() + { + return (TransactionCoordinatorApi) applicationServerSpringContext.getBean(ITransactionCoordinatorApi.class); } private void rollbackPreparedDatabaseTransactions() throws Exception @@ -602,12 +681,44 @@ public class Integration2PCTest extends AbstractIntegrationTest try (Connection connection = applicationServerSpringContext.getBean(DataSource.class).getConnection(); Statement statement = connection.createStatement()) { - statement.execute("DELETE FROM experiments WHERE code LIKE '" + CODE_PREFIX + "%'"); - statement.execute("DELETE FROM projects WHERE code LIKE '" + CODE_PREFIX + "%'"); - statement.execute("DELETE FROM spaces WHERE code LIKE '" + CODE_PREFIX + "%'"); + statement.execute("DELETE FROM experiments WHERE code LIKE '" + ENTITY_CODE_PREFIX + "%'"); + statement.execute("DELETE FROM projects WHERE code LIKE '" + ENTITY_CODE_PREFIX + "%'"); + statement.execute("DELETE FROM spaces WHERE code LIKE '" + ENTITY_CODE_PREFIX + "%'"); } } + private static SpaceCreation createSpaceCreation(){ + SpaceCreation spaceCreation = new SpaceCreation(); + spaceCreation.setCode(ENTITY_CODE_PREFIX + UUID.randomUUID()); + return spaceCreation; + } + + private static ProjectCreation createProjectCreation(String spaceCode){ + ProjectCreation projectCreation = new ProjectCreation(); + projectCreation.setSpaceId(new SpacePermId(spaceCode)); + projectCreation.setCode(ENTITY_CODE_PREFIX + UUID.randomUUID()); + return projectCreation; + } + + private static ExperimentCreation createExperimentCreation(String spaceCode, String projectCode, String experimentTypeCode){ + ExperimentCreation experimentCreation = new ExperimentCreation(); + experimentCreation.setProjectId(new ProjectIdentifier(spaceCode, projectCode)); + experimentCreation.setCode(ENTITY_CODE_PREFIX + UUID.randomUUID()); + experimentCreation.setTypeId(experimentTypeCode != null ? new EntityTypePermId(experimentTypeCode) : null); + return experimentCreation; + } + + private static WriteData createWriteData() + { + WriteData writeData = new WriteData(); + writeData.owner = "test-owner-" + UUID.randomUUID(); + writeData.source = "test-source-" + UUID.randomUUID(); + writeData.content = "test-content"; + writeData.bytes = writeData.content.getBytes(StandardCharsets.UTF_8); + writeData.md5 = calculateMD5(writeData.bytes); + return writeData; + } + private static byte[] calculateMD5(byte[] data) { try @@ -621,4 +732,17 @@ public class Integration2PCTest extends AbstractIntegrationTest } } + private static class WriteData + { + public String owner; + + public String source; + + public String content; + + public byte[] bytes; + + public byte[] md5; + } + } diff --git a/test-integration/sourceTest/java/ch/ethz/sis/openbis/systemtests/common/AbstractIntegrationTest.java b/test-integration/sourceTest/java/ch/ethz/sis/openbis/systemtests/common/AbstractIntegrationTest.java index f47f56b642e9905a5fc268ff713e02d56f574252..0e632b698d304316143a4c6d725b905b4a97ac2a 100644 --- a/test-integration/sourceTest/java/ch/ethz/sis/openbis/systemtests/common/AbstractIntegrationTest.java +++ b/test-integration/sourceTest/java/ch/ethz/sis/openbis/systemtests/common/AbstractIntegrationTest.java @@ -48,6 +48,7 @@ import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.springframework.beans.factory.xml.XmlBeanFactory; +import org.springframework.cache.ehcache.EhCacheManagerFactoryBean; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.core.io.FileSystemResource; import org.springframework.remoting.rmi.CodebaseAwareObjectInputStream; @@ -104,7 +105,7 @@ public abstract class AbstractIntegrationTest cleanupApplicationServerFolders(); cleanupAfsServerFolders(); - startApplicationServer(); + startApplicationServer(true); startApplicationServerProxy(); startAfsServer(); startAfsServerProxy(); @@ -119,30 +120,6 @@ public abstract class AbstractIntegrationTest shutdownAfsServerProxy(); } - private void shutdownApplicationServer() - { - applicationServer.setStopAtShutdown(true); - log("Shut down application server."); - } - - private void shutdownApplicationServerProxy() - { - applicationServerProxy.setStopAtShutdown(true); - log("Shut down application server proxy."); - } - - private void shutdownAfsServer() throws Exception - { - afsServer.shutdown(false); - log("Shut down afs server."); - } - - private void shutdownAfsServerProxy() - { - afsServerProxy.setStopAtShutdown(true); - log("Shut down afs server proxy."); - } - @BeforeMethod public void beforeMethod(Method method) { @@ -165,7 +142,7 @@ public abstract class AbstractIntegrationTest private void cleanupApplicationServerFolders() throws Exception { - Properties configuration = getApplicationServerConfiguration(); + Properties configuration = getApplicationServerConfiguration(true); String transactionLogFolder = configuration.getProperty(TransactionConfiguration.TRANSACTION_LOG_FOLDER_PATH_PROPERTY_NAME); cleanupFolderSafely(transactionLogFolder); @@ -209,9 +186,10 @@ public abstract class AbstractIntegrationTest } } - private void startApplicationServer() throws Exception + private void startApplicationServer(boolean createDatabase) throws Exception { - Properties configuration = getApplicationServerConfiguration(); + log("Starting application server."); + Properties configuration = getApplicationServerConfiguration(createDatabase); for (Object key : configuration.keySet()) { @@ -246,10 +224,12 @@ public abstract class AbstractIntegrationTest server.start(); AbstractIntegrationTest.applicationServer = server; + log("Started application server."); } private void startApplicationServerProxy() throws Exception { + log("Starting application server proxy."); Server server = new Server(); HttpConfiguration httpConfig = new HttpConfiguration(); ServerConnector connector = @@ -299,18 +279,22 @@ public abstract class AbstractIntegrationTest server.start(); AbstractIntegrationTest.applicationServerProxy = server; + log("Started application server proxy."); } private void startAfsServer() throws Exception { + log("Starting afs server."); Configuration configuration = getAfsServerConfiguration(); DummyServerObserver dummyServerObserver = new DummyServerObserver(); AbstractIntegrationTest.afsServer = new ch.ethz.sis.afsserver.server.Server<>(configuration, dummyServerObserver, dummyServerObserver); + log("Started afs server."); } private void startAfsServerProxy() throws Exception { + log("Starting afs server proxy."); Server server = new Server(); HttpConfiguration httpConfig = new HttpConfiguration(); ServerConnector connector = @@ -375,13 +359,54 @@ public abstract class AbstractIntegrationTest server.start(); AbstractIntegrationTest.afsServerProxy = server; + log("Started afs server proxy."); + } + + private void shutdownApplicationServer() throws Exception + { + // manually destroy EHCache (without it the new AS won't start in the same VM) + applicationServerSpringContext.getBean(EhCacheManagerFactoryBean.class).destroy(); + applicationServer.stop(); + log("Shut down application server."); + } + + private void shutdownApplicationServerProxy() + { + applicationServerProxy.setStopAtShutdown(true); + log("Shut down application server proxy."); + } + + private void shutdownAfsServer() throws Exception + { + afsServer.shutdown(false); + log("Shut down afs server."); + } + + private void shutdownAfsServerProxy() + { + afsServerProxy.setStopAtShutdown(true); + log("Shut down afs server proxy."); + } + + public void restartApplicationServer() throws Exception + { + log("Restarting application server."); + shutdownApplicationServer(); + startApplicationServer(false); + } + + public void restartAfsServer() throws Exception + { + log("Restarting afs server."); + shutdownAfsServer(); + startAfsServer(); } - private Properties getApplicationServerConfiguration() throws Exception + private Properties getApplicationServerConfiguration(boolean createDatabase) throws Exception { Properties configuration = new Properties(); configuration.load(new FileInputStream("../server-application-server/source/java/service.properties")); - configuration.setProperty("database.create-from-scratch", "true"); + configuration.setProperty("database.create-from-scratch", String.valueOf(createDatabase)); configuration.setProperty("database.kind", "integration"); configuration.setProperty("script-folder", "../server-application-server/source"); configuration.setProperty(TransactionConfiguration.COORDINATOR_KEY_PROPERTY_NAME, TEST_TRANSACTION_COORDINATOR_KEY);