/** * Copyright 2011-2012 Akiban Technologies, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.persistit; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.io.File; import java.io.RandomAccessFile; import java.util.List; import org.junit.Test; import com.persistit.exception.CorruptVolumeException; import com.persistit.exception.InUseException; import com.persistit.exception.InvalidVolumeSpecificationException; import com.persistit.exception.VolumeFullException; import com.persistit.unit.UnitTestProperties; public class VolumeTest extends PersistitUnitTestCase { @Test public void testHollowVolume() throws Exception { final Volume volume = new Volume("Sassafras", 12345); assertEquals("Sassafras", volume.getName()); assertEquals(12345, volume.getId()); volume.setId(12345); volume.setId(0); volume.setId(12346); try { volume.setId(12345); fail("Can't change volume id"); } catch (final IllegalStateException e) { // ok } try { assertNull(volume.getStructure()); } catch (final IllegalStateException e) { // ok } try { assertNull(volume.getStorage()); } catch (final IllegalStateException e) { // ok } try { assertNull(volume.getStatistics()); } catch (final IllegalStateException e) { // ok } } @Test public void testCreateOpenVolume() throws Exception { final VolumeSpecification vs = validVolumeSpecification("${datapath}/vtest, pageSize:16k, initialSize:1k, maximumSize:1m, extensionSize:1K, create"); final Volume volume1 = new Volume(vs); volume1.open(_persistit); final long id = volume1.getId(); _persistit.flush(); _persistit.copyBackPages(); volume1.close(); final Volume volume2 = new Volume(vs); volume2.open(_persistit); assertEquals(id, volume2.getId()); final long maxPages = volume2.getSpecification().getMaximumPages(); assertEquals(1024 * 1024 / 16384, maxPages); for (int i = 2; i < 1000; i++) { try { volume2.getStorage().claimHeadBuffer(); assertEquals(i, volume2.getStorage().allocNewPage()); volume2.getStorage().releaseHeadBuffer(); } catch (final VolumeFullException e) { assertEquals(maxPages, i); break; } } assertEquals(maxPages, volume2.getStorage().getNextAvailablePage()); assertEquals(maxPages, volume2.getStorage().getExtentedPageCount()); final File file = new File(volume2.getPath()); assertEquals(maxPages * 16384, file.length()); volume2.close(); final RandomAccessFile raf = new RandomAccessFile(file, "rw"); raf.write(new byte[16]); raf.close(); final Volume volume3 = new Volume(vs); try { volume3.open(_persistit); fail("Volume should have been corrupt"); } catch (final CorruptVolumeException e) { // okay } } @Test public void testDeleteVolume() throws Exception { final VolumeSpecification vs = validVolumeSpecification("${datapath}/vtest, pageSize:16k, initialSize:1k, maximumSize:1m, extensionSize:1K, create"); final Volume volume1 = new Volume(vs); volume1.open(_persistit); final File file = new File(volume1.getPath()); assertEquals(32768, file.length()); assertTrue(_persistit.deleteVolume("vtest")); assertTrue(volume1.isClosed()); assertTrue(!file.exists()); } @Test public void testDottedVolumeName() throws Exception { final VolumeSpecification vs = validVolumeSpecification("${datapath}/.thisStuffWontBeIgnored, pageSize:16k, initialSize:1k, maximumSize:1m, extensionSize:1K, create"); final Volume volume1 = _persistit.loadVolume(vs); final Exchange ex = _persistit.getExchange(".thisStuffWontBeIgnored", "emptyTreeTest", true); assertEquals(ex.getVolume(), volume1); } @Test public void testVolumeSpecification() throws Exception { VolumeSpecification vs; VolumeSpecification vs2; vs = validVolumeSpecification("/a/b/c,name: crabcake, pageSize: 16384, initialSize: 10m, maximumSize: 100m, extensionSize: 10m, create"); vs = validVolumeSpecification("/a/b/c,name:crabcake,pageSize:16384,initialSize:10m,maximumSize:100m,extensionSize:10m,create"); assertEquals("crabcake", vs.getName()); assertEquals(16384, vs.getPageSize()); assertEquals(10 * 1024 * 1024 / 16384, vs.getInitialPages()); assertEquals(10 * 1024 * 1024, vs.getInitialSize()); assertTrue(vs.isCreate()); assertTrue(vs.isAliased()); assertFalse(vs.isCreateOnly()); assertFalse(vs.isReadOnly()); vs2 = validVolumeSpecification(vs.toString()); assertEquals("Parse of toString should be equal", vs, vs2); vs = validVolumeSpecification("/a/b/c"); assertEquals("c", vs.getName()); assertEquals(-1, vs.getPageSize()); assertFalse(vs.isCreate()); assertFalse(vs.isCreateOnly()); assertFalse(vs.isReadOnly()); vs2 = validVolumeSpecification(vs.toString()); assertEquals("Parse of toString should be equal", vs, vs2); vs = validVolumeSpecification("/a/b/c.v01"); assertEquals("c", vs.getName()); vs2 = validVolumeSpecification(vs.toString()); assertEquals("Parse of toString should be equal", vs, vs2); vs = validVolumeSpecification("/a/b/c.d.v01"); assertEquals("c.d", vs.getName()); vs2 = validVolumeSpecification(vs.toString()); assertEquals("Parse of toString should be equal", vs, vs2); invalidVolumeSpecification("/a/b/c,name:crabcake,pagesize:16383,initialsize:10m,maximumsize:100m,extensionsize:10m,create"); invalidVolumeSpecification("/a/b/c;name:crabcake,pagesize:16384,initialsize:10m,maximumsize:100m,extensionsize:10m,create"); invalidVolumeSpecification("/a/b/c,name:crabcake,pagesize:16384,initialsize:10p,maximumsize:100p,extensionsize:10p,create"); invalidVolumeSpecification("/a/b/c,name:crabcake,pagesize:16384,initialsize:10m,maximumsize:100m,extensionsize:10m,create,readOnly"); vs = validVolumeSpecification("/a/b/c,pageSize: 16384, initialSize: 10m, maximumSize: 100m, extensionSize: 10m, create"); assertEquals(10 * 1024 * 1024 / 16384, vs.getInitialPages()); assertEquals(10 * 1024 * 1024, vs.getInitialSize()); vs.setInitialPages(42); vs.setExtensionPages(58); vs.setMaximumPages(Integer.MAX_VALUE); vs2 = validVolumeSpecification(vs.toString()); assertEquals("Parse of toString should be equal", vs, vs2); vs.setMaximumSize(Long.MAX_VALUE); vs.setInitialSize(Long.MAX_VALUE / 7); vs.setExtensionSize(Long.MAX_VALUE / 23); assertEquals(Long.MAX_VALUE / 23 / 16384, vs.getExtensionPages()); vs2 = validVolumeSpecification(vs.toString()); assertEquals("Parse of toString should be equal", vs, vs2); } @Test public void volumeLoadAndSaveGlobalTimestamp() throws Exception { final long MARKER = 123456789L; _persistit.getTimestampAllocator().updateTimestamp(MARKER); final VolumeSpecification vs = validVolumeSpecification("${datapath}/testGlobalTimestamp, pageSize:16k, initialSize:1k, maximumSize:1m, extensionSize:1K, create"); final Volume vol1 = _persistit.loadVolume(vs); vol1.close(); final Volume vol2 = _persistit.loadVolume(vs); final long statTimestamp = vol2.getStatistics().getLastGlobalTimestamp(); // Greater than is ok (other activity may have occurred) if (statTimestamp < MARKER) { assertEquals("Saved and loaded timestamp", MARKER, statTimestamp); } } @Test(expected = CorruptVolumeException.class) public void volumeFromFutureIsRejected() throws Exception { final int RECORDS = 100; // Make it more obvious if when we jump backwards _persistit.getTimestampAllocator().bumpTimestamp(1000000); // Write records to check on later final Exchange ex = _persistit.getExchange(UnitTestProperties.VOLUME_NAME, "VolumeTest", true); final Transaction txn = _persistit.getTransaction(); txn.begin(); for (int i = 0; i < RECORDS; ++i) { ex.clear().append(i).getValue().put(i); ex.store(); } txn.commit(); txn.end(); _persistit.releaseExchange(ex); _persistit.flush(); _persistit.copyBackPages(); final List<File> journalFiles = _persistit.getJournalManager().unitTestGetAllJournalFiles(); _persistit.crash(); /* * Worst case (or slipped finger) scenario of missing journal files */ for (final File file : journalFiles) { final boolean success = file.delete(); assertEquals("Deleted journal file " + file.getName(), true, success); } _persistit = new Persistit(_config); } @Test public void timeoutWhenPageIsInUse() throws Exception { final Exchange exchange = _persistit.getExchange(UnitTestProperties.VOLUME_NAME, "VolumeTest", true); final Buffer buffer = exchange.getBufferPool().get(exchange.getVolume(), 1, false, true); try { final long start = System.currentTimeMillis(); try { exchange.getVolume().close(5000); fail("Expect an InUseException"); } catch (final InUseException e) { final long elapsed = System.currentTimeMillis() - start; assertTrue("Expected InUseException to happen at 5000 ms but was " + elapsed, elapsed > 4000 && elapsed < 10000); } } finally { buffer.release(); } } private VolumeSpecification validVolumeSpecification(final String specification) throws Exception { try { return _persistit.getConfiguration().volumeSpecification(specification); } catch (final InvalidVolumeSpecificationException e) { fail(specification + " should be valid: " + e); return null; } } private void invalidVolumeSpecification(final String specification) throws InvalidVolumeSpecificationException { try { new VolumeSpecification(specification); fail(); } catch (final InvalidVolumeSpecificationException e) { // ok } } }