Skip to content

Commit

Permalink
feat: implement SingleFileIndexStorageManager
Browse files Browse the repository at this point in the history
  • Loading branch information
sepgh committed Jul 7, 2024
1 parent ac4f9d4 commit dea1c90
Show file tree
Hide file tree
Showing 3 changed files with 500 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.github.sepgh.testudo.storage;

import com.github.sepgh.testudo.EngineConfig;
import com.github.sepgh.testudo.index.Pointer;
import com.github.sepgh.testudo.storage.header.HeaderManager;
import com.github.sepgh.testudo.storage.pool.FileHandlerPool;
import com.github.sepgh.testudo.storage.pool.ManagedFileHandler;
import com.github.sepgh.testudo.utils.FileUtils;

import javax.annotation.Nullable;
import java.io.IOException;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.file.Path;
import java.util.concurrent.ExecutionException;

public class SingleFileIndexStorageManager extends BaseFileIndexStorageManager {

public SingleFileIndexStorageManager(Path path, @Nullable String customName, HeaderManager headerManager, EngineConfig engineConfig, FileHandlerPool fileHandlerPool) throws IOException, ExecutionException, InterruptedException {
super(path, customName, headerManager, engineConfig, fileHandlerPool);
}

public SingleFileIndexStorageManager(Path path, @Nullable String customName, HeaderManager headerManager, EngineConfig engineConfig, FileHandlerPool fileHandlerPool, int binarySpace) throws IOException, ExecutionException, InterruptedException {
super(path, customName, headerManager, engineConfig, fileHandlerPool, binarySpace);
}

public SingleFileIndexStorageManager(Path path, HeaderManager headerManager, EngineConfig engineConfig, FileHandlerPool fileHandlerPool, int binarySpaceMax) throws IOException, ExecutionException, InterruptedException {
super(path, headerManager, engineConfig, fileHandlerPool, binarySpaceMax);
}

public SingleFileIndexStorageManager(Path path, HeaderManager headerManager, EngineConfig engineConfig, int binarySpaceMax) throws IOException, ExecutionException, InterruptedException {
super(path, headerManager, engineConfig, binarySpaceMax);
}

protected Path getIndexFilePath(int table, int chunk) {
if (customName == null)
return Path.of(path.toString(), String.format("%s.bin", INDEX_FILE_NAME));
return Path.of(path.toString(), String.format("%s.%s.bin", INDEX_FILE_NAME, customName));
}

protected Pointer getAllocatedSpaceForNewNode(int tableId, int chunk) throws IOException, ExecutionException, InterruptedException {
ManagedFileHandler managedFileHandler = this.getManagedFileHandler(tableId, 0);
AsynchronousFileChannel asynchronousFileChannel = managedFileHandler.getAsynchronousFileChannel();

Long position = FileUtils.allocate(asynchronousFileChannel, this.getIndexGrowthAllocationSize()).get();
managedFileHandler.close();
return new Pointer(Pointer.TYPE_NODE, position, chunk);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
package com.github.sepgh.testudo.index.tree.storing;

import com.github.sepgh.testudo.EngineConfig;
import com.github.sepgh.testudo.exception.IndexExistsException;
import com.github.sepgh.testudo.exception.InternalOperationException;
import com.github.sepgh.testudo.helper.IndexFileDescriptor;
import com.github.sepgh.testudo.index.IndexManager;
import com.github.sepgh.testudo.index.Pointer;
import com.github.sepgh.testudo.index.tree.node.AbstractLeafTreeNode;
import com.github.sepgh.testudo.index.tree.node.AbstractTreeNode;
import com.github.sepgh.testudo.index.tree.node.cluster.ClusterBPlusTreeIndexManager;
import com.github.sepgh.testudo.index.tree.node.data.ImmutableBinaryObjectWrapper;
import com.github.sepgh.testudo.index.tree.node.data.LongImmutableBinaryObjectWrapper;
import com.github.sepgh.testudo.index.tree.node.data.PointerImmutableBinaryObjectWrapper;
import com.github.sepgh.testudo.storage.BTreeSizeCalculator;
import com.github.sepgh.testudo.storage.InMemoryHeaderManager;
import com.github.sepgh.testudo.storage.SingleFileIndexStorageManager;
import com.github.sepgh.testudo.storage.header.Header;
import com.github.sepgh.testudo.storage.header.HeaderManager;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ExecutionException;

import static com.github.sepgh.testudo.storage.SingleFileIndexStorageManager.INDEX_FILE_NAME;

public class MultiTableBPlusTreeIndexManagerSingleStorageManagerTestCase {
private Path dbPath;
private EngineConfig engineConfig;
private Header header;
private final int degree = 4;

@BeforeEach
public void setUp() throws IOException {
dbPath = Files.createTempDirectory("TEST_MultiTableBPlusTreeIndexManagerSingleStorageManagerTestCase");
engineConfig = EngineConfig.builder()
.bTreeDegree(degree)
.bTreeGrowthNodeAllocationCount(10)
.build();
engineConfig.setBTreeMaxFileSize(2 * 15L * BTreeSizeCalculator.getClusteredBPlusTreeSize(degree, LongImmutableBinaryObjectWrapper.BYTES));

byte[] writingBytes = new byte[2 * 13 * BTreeSizeCalculator.getClusteredBPlusTreeSize(degree, LongImmutableBinaryObjectWrapper.BYTES)];
Path indexPath = Path.of(dbPath.toString(), String.format("%s.bin", INDEX_FILE_NAME));
Files.write(indexPath, writingBytes, StandardOpenOption.WRITE, StandardOpenOption.CREATE);

header = Header.builder()
.database("sample")
.tables(
Arrays.asList(
Header.Table.builder()
.id(1)
.name("test")
.chunks(
Collections.singletonList(
Header.IndexChunk.builder()
.chunk(0)
.offset(0)
.build()
)
)
.root(
Header.IndexChunk.builder()
.chunk(0)
.offset(0)
.build()
)
.initialized(true)
.build(),
Header.Table.builder()
.id(2)
.name("test2")
.chunks(
Collections.singletonList(
Header.IndexChunk.builder()
.chunk(0)
.offset(12L * BTreeSizeCalculator.getClusteredBPlusTreeSize(degree, LongImmutableBinaryObjectWrapper.BYTES))
.build()
)
)
.root(
Header.IndexChunk.builder()
.chunk(0)
.offset(12L * BTreeSizeCalculator.getClusteredBPlusTreeSize(degree, LongImmutableBinaryObjectWrapper.BYTES))
.build()
)
.initialized(true)
.build()
)
)
.build();

Assertions.assertTrue(header.getTableOfId(1).isPresent());
Assertions.assertTrue(header.getTableOfId(1).get().getIndexChunk(0).isPresent());
Assertions.assertTrue(header.getTableOfId(2).isPresent());
Assertions.assertTrue(header.getTableOfId(2).get().getIndexChunk(0).isPresent());
}

@AfterEach
public void destroy() throws IOException {
Path indexPath0 = Path.of(dbPath.toString(), String.format("%s.bin", INDEX_FILE_NAME));
Files.delete(indexPath0);
}


/**
*
* The B+Tree in this test will include numbers from [1-12] added respectively
* The shape of the tree will be like below, and the test verifies that
* The test validates the tree for 2 tables in same database
* 007
* ├── .
* │ ├── 001
* │ └── 002
* ├── 003
* │ ├── 003
* │ └── 004
* ├── 005
* │ ├── 005
* │ └── 006
* ├── .
* │ ├── 007
* │ └── 008
* ├── 009
* │ ├── 009
* │ └── 010
* └── 0011
* ├── 011
* └── 012
*/
@Test
public void testMultiSplitAddIndex() throws IOException, ExecutionException, InterruptedException, ImmutableBinaryObjectWrapper.InvalidBinaryObjectWrapperValue, IndexExistsException, InternalOperationException {

List<Long> testIdentifiers = Arrays.asList(1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L, 10L, 11L, 12L);
Pointer samplePointer = new Pointer(Pointer.TYPE_DATA, 100, 0);

for (int tableId = 1; tableId <= 2; tableId++){
HeaderManager headerManager = new InMemoryHeaderManager(header);
SingleFileIndexStorageManager singleFileIndexStorageManager = new SingleFileIndexStorageManager(dbPath, headerManager, engineConfig, BTreeSizeCalculator.getClusteredBPlusTreeSize(degree, LongImmutableBinaryObjectWrapper.BYTES));
IndexManager<Long, Pointer> indexManager = new ClusterBPlusTreeIndexManager<>(degree, singleFileIndexStorageManager, new LongImmutableBinaryObjectWrapper());



AbstractTreeNode<Long> lastTreeNode = null;
for (long testIdentifier : testIdentifiers) {
lastTreeNode = indexManager.addIndex(tableId, testIdentifier, samplePointer);
}

Assertions.assertTrue(lastTreeNode.isLeaf());
Assertions.assertEquals(2, lastTreeNode.getKeyList(degree, PointerImmutableBinaryObjectWrapper.BYTES).size());
Assertions.assertEquals(samplePointer.getPosition(), ((AbstractLeafTreeNode<Long, Pointer>) lastTreeNode).getKeyValues(degree).next().value().getPosition());

StoredTreeStructureVerifier.testOrderedTreeStructure(singleFileIndexStorageManager, tableId, 1, degree);
}

}


/**
*
* The B+Tree in this test will include numbers from [1-12] added respectively
* The shape of the tree will be like below, and the test verifies that
* The test validates the tree for 2 tables in same database
* 009
* ├── .
* │ ├── 001
* │ └── 002
* ├── 003
* │ ├── 003
* │ ├── 004
* │ └── 005
* ├── 006
* │ ├── 006
* │ ├── 007
* │ └── 008
* ├── .
* │ ├── 009
* │ └── 010
* └── 011
* ├── 011
* └── 012
*/
@Test
public void testMultiSplitAddIndex2() throws IOException, ExecutionException, InterruptedException, ImmutableBinaryObjectWrapper.InvalidBinaryObjectWrapperValue, IndexExistsException, InternalOperationException {

List<Long> testIdentifiers = Arrays.asList(1L, 4L, 9L, 6L, 10L, 8L, 3L, 2L, 11L, 5L, 7L, 12L);
Pointer samplePointer = new Pointer(Pointer.TYPE_DATA, 100, 0);
HeaderManager headerManager = new InMemoryHeaderManager(header);
SingleFileIndexStorageManager singleFileIndexStorageManager = new SingleFileIndexStorageManager(dbPath, headerManager, engineConfig, BTreeSizeCalculator.getClusteredBPlusTreeSize(degree, LongImmutableBinaryObjectWrapper.BYTES));
IndexManager<Long, Pointer> indexManager = new ClusterBPlusTreeIndexManager<>(degree, singleFileIndexStorageManager, new LongImmutableBinaryObjectWrapper());

IndexFileDescriptor indexFileDescriptor = new IndexFileDescriptor(
AsynchronousFileChannel.open(
Path.of(dbPath.toString(), String.format("%s.%d", INDEX_FILE_NAME, 0)),
StandardOpenOption.READ,
StandardOpenOption.WRITE,
StandardOpenOption.CREATE
),
headerManager,
engineConfig
);

for (int tableId = 1; tableId <= 2; tableId++){

AbstractTreeNode<Long> lastTreeNode = null;
for (long testIdentifier : testIdentifiers) {
lastTreeNode = indexManager.addIndex(tableId, testIdentifier, samplePointer);
indexFileDescriptor.describe(new LongImmutableBinaryObjectWrapper());
}

Assertions.assertTrue(lastTreeNode.isLeaf());
Assertions.assertEquals(2, lastTreeNode.getKeyList(degree, PointerImmutableBinaryObjectWrapper.BYTES).size());
Assertions.assertEquals(samplePointer.getPosition(), ((AbstractLeafTreeNode<Long, Pointer>) lastTreeNode).getKeyValues(degree).next().value().getPosition());

StoredTreeStructureVerifier.testUnOrderedTreeStructure1(singleFileIndexStorageManager, tableId, 1, degree);

}

}

}
Loading

0 comments on commit dea1c90

Please sign in to comment.