diff --git a/block-storage/src/main/java/org/lucares/pdb/diskstorage/DiskStorage.java b/block-storage/src/main/java/org/lucares/pdb/diskstorage/DiskStorage.java index 7e2e51a..acace23 100644 --- a/block-storage/src/main/java/org/lucares/pdb/diskstorage/DiskStorage.java +++ b/block-storage/src/main/java/org/lucares/pdb/diskstorage/DiskStorage.java @@ -17,6 +17,7 @@ public class DiskStorage implements AutoCloseable { private static final Logger LOGGER = LoggerFactory.getLogger(DiskStorage.class); private static final long FREE_LIST_ROOT_OFFSET = 0; + private static final long NO_POINTER = 0; private static final int FREE_LIST_NEXT_POINTER = 0; private static final int FREE_LIST_PREV_POINTER = 8; private static final int FREE_LIST_SIZE = 16; @@ -31,11 +32,13 @@ public class DiskStorage implements AutoCloseable { fileChannel = FileChannel.open(databaseFile, StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE); + initIfNew(); + } + + private void initIfNew() throws IOException { if (fileChannel.size() == 0) { // file is new -> add root of the free list - // TODO implement a real free list - final var src = ByteBuffer.allocate(8); - fileChannel.write(src, 0); + writeFreeListRootNodePosition(NO_POINTER); } } @@ -233,4 +236,14 @@ public class DiskStorage implements AutoCloseable { freeListFirstBlock.putLong(0, freeListRootNodePosition); fileChannel.write(freeListFirstBlock, FREE_LIST_ROOT_OFFSET); } + + public synchronized void ensureAlignmentForNewBlocks(final int alignment) throws IOException { + final long size = fileChannel.size(); + final int alignmentMismatch = Math.floorMod(size, alignment); + if (alignmentMismatch != 0) { + // The next allocated block would not be aligned. Therefore we allocate a + // throw-away block. + allocateNewBlock(alignment - alignmentMismatch); + } + } } diff --git a/data-store/src/main/java/org/lucares/pdb/datastore/internal/DataStore.java b/data-store/src/main/java/org/lucares/pdb/datastore/internal/DataStore.java index 6f30a7e..0f029aa 100644 --- a/data-store/src/main/java/org/lucares/pdb/datastore/internal/DataStore.java +++ b/data-store/src/main/java/org/lucares/pdb/datastore/internal/DataStore.java @@ -66,6 +66,7 @@ public class DataStore implements AutoCloseable { listingFilePath = storageBasePath.resolve("listing.csv"); diskStorageFilePath = storageBasePath.resolve("data.bs"); diskStorage = new DiskStorage(diskStorageFilePath); + diskStorage.ensureAlignmentForNewBlocks(BSFile.BLOCK_SIZE); initListingFileIfNotExists(); init(diskStorage); listingFile = new RandomAccessFile(listingFilePath.toFile(), "rw"); diff --git a/data-store/src/test/java/org/lucares/pdb/datastore/internal/DataStoreTest.java b/data-store/src/test/java/org/lucares/pdb/datastore/internal/DataStoreTest.java index 943827c..a39f530 100644 --- a/data-store/src/test/java/org/lucares/pdb/datastore/internal/DataStoreTest.java +++ b/data-store/src/test/java/org/lucares/pdb/datastore/internal/DataStoreTest.java @@ -9,6 +9,7 @@ import java.util.List; import java.util.Map; import org.lucares.pdb.api.Tags; +import org.lucares.pdb.blockstorage.BSFile; import org.lucares.pdb.datastore.Doc; import org.lucares.utils.CollectionUtils; import org.lucares.utils.file.FileUtils; @@ -106,6 +107,14 @@ public class DataStoreTest { Assert.assertEquals(docsFlamingoJennifer.size(), 1, "doc for docsFlamingoJennifer"); } + public void testBlockAlignment() throws IOException { + + dataStore = new DataStore(dataDirectory); + final Tags eagleTim = Tags.create("bird", "eagle", "name", "Tim"); + final long eagleTimBlockOffset = dataStore.createNewFile(eagleTim); + Assert.assertEquals(eagleTimBlockOffset % BSFile.BLOCK_SIZE, 0); + } + private void assertSearch(final String query, final Tags... tags) { final List actualDocs = dataStore.search(query); final List actual = CollectionUtils.map(actualDocs, Doc::getRootBlockNumber);