| | |
| | | */ |
| | | package org.opends.server.backends.jdbc; |
| | | |
| | | import org.forgerock.opendj.ldap.ByteString; |
| | | import org.forgerock.opendj.server.config.server.JDBCBackendCfg; |
| | | import org.opends.server.DirectoryServerTestCase; |
| | | import org.opends.server.backends.pluggable.spi.TreeName; |
| | | import org.opends.server.backends.pluggable.spi.AccessMode; |
| | | import org.opends.server.backends.pluggable.spi.Importer; |
| | | import org.testng.annotations.AfterClass; |
| | |
| | | import java.sql.Driver; |
| | | import java.sql.DriverManager; |
| | | import java.sql.DriverPropertyInfo; |
| | | import java.sql.PreparedStatement; |
| | | import java.sql.SQLException; |
| | | import java.sql.SQLTimeoutException; |
| | | import java.util.ArrayDeque; |
| | |
| | | |
| | | import static org.mockito.Mockito.any; |
| | | import static org.mockito.Mockito.anyInt; |
| | | import static org.mockito.Mockito.anyString; |
| | | import static org.mockito.Mockito.doAnswer; |
| | | import static org.mockito.Mockito.doNothing; |
| | | import static org.mockito.Mockito.doThrow; |
| | |
| | | final String url = StubDriver.PREFIX + "import-unchecked-commit"; |
| | | final Connection parent = mock(Connection.class); |
| | | when(parent.isValid(anyInt())).thenReturn(true); |
| | | // the statement of the write below, which is what gives the commit of close() something to do: |
| | | // a connection an import wrote nothing through is not committed at all (#891), so an import |
| | | // that writes nothing would reach neither the Error this test injects nor the return it is about |
| | | when(parent.prepareStatement(anyString())).thenReturn(mock(PreparedStatement.class)); |
| | | doThrow(new Error("out of memory while importing")).when(parent).commit(); |
| | | stub.answerWith(parent); |
| | | final JDBCBackendCfg cfg = mock(JDBCBackendCfg.class); |
| | |
| | | final JDBCStorage storage = new JDBCStorage(cfg, null); |
| | | storage.open(AccessMode.READ_WRITE); |
| | | final Importer importer = storage.startImport(); |
| | | importer.put(new TreeName("dc=example,dc=com", "id2entry"), |
| | | ByteString.valueOfUtf8("key"), ByteString.valueOfUtf8("value")); |
| | | |
| | | try { |
| | | importer.close(); |
| | | fail("the failure of the commit was not reported"); |
| | | } catch (Error expected) { |
| | | // reported to the caller, which is what an Error out of an import has to be |
| | | // reported to the caller, which is what an Error out of an import has to be. Asserted |
| | | // rather than accepted whole: an AssertionError of the fail() above is an Error too, and |
| | | // would otherwise be caught here and read as the injected one |
| | | assertEquals(expected.getMessage(), "out of memory while importing"); |
| | | } |
| | | |
| | | assertEquals(CachedConnection.poolOf(url).idleCount(), 1, "the import kept the connection of the pool"); |
| | |
| | | assertEquals(CachedConnection.deadlineOf(startedAt, 60), startedAt + 60_000); |
| | | } |
| | | |
| | | /** |
| | | * A borrow that carries a bound of its own waits for the shorter of the two, whichever way the |
| | | * deployment spelled a wait with no bound at all. |
| | | * <p> |
| | | * Only an import carries one: it holds a connection per tree it writes until it ends (#891), so |
| | | * the pool it waits at may be full of nothing but its own connections and the wait would be a |
| | | * deadlock rather than a queue. It has somewhere to go when the wait runs out - the tree is |
| | | * written through a connection the import already holds - so a long wait of the deployment is |
| | | * capped rather than merely replaced: paid over again for every tree the pool has nothing to |
| | | * spare for, it would be the duration of an import rather than a bound on it. |
| | | */ |
| | | @Test |
| | | public void testABorrowWithABoundOfItsOwnWaitsForTheShorterOfTheTwo() { |
| | | assertEquals(CachedConnection.boundedWait(30, 60), 30, "the wait of the deployment was inside the bound"); |
| | | assertEquals(CachedConnection.boundedWait(600, 60), 60, "a long finite wait was not capped"); |
| | | assertEquals(CachedConnection.boundedWait(0, 60), 60, "0 stands for a wait with no bound"); |
| | | // the other spelling of a wait with no bound: seconds enough that the milliseconds they |
| | | // stand for do not fit in a long, which the deadline of the borrow reads as forever |
| | | assertEquals(CachedConnection.boundedWait(Long.MAX_VALUE / 1000, 60), 60, |
| | | "a wait whose milliseconds overflow a long is unbounded too"); |
| | | assertEquals(CachedConnection.boundedWait(Long.MAX_VALUE, 60), 60); |
| | | // ... and a borrow that carries no bound of its own takes the wait of the deployment whole |
| | | assertEquals(CachedConnection.boundedWait(600, 0), 600); |
| | | assertEquals(CachedConnection.boundedWait(0, 0), 0); |
| | | } |
| | | |
| | | /** The connection string holds the credentials of the backend: a stall report must not carry them. */ |
| | | @Test |
| | | public void testLoggedConnectionStringCarriesNoCredentials() throws Exception { |