Un-promisify BTreeDB, since this is job of higher abstractions

This commit is contained in:
DBotThePony 2024-02-18 10:46:18 +07:00
parent 486e8834d6
commit 6dc434fa04
Signed by: DBot
GPG Key ID: DCC23B5715498507
4 changed files with 31 additions and 58 deletions
.gitignore
src
main/kotlin/ru/dbotthepony/kommons/io
test/kotlin/ru/dbotthepony/kommons/test

3
.gitignore vendored
View File

@ -39,4 +39,5 @@ bin/
.vscode/ .vscode/
### Mac OS ### ### Mac OS ###
.DS_Store .DS_Store
/dbtest.bdb

View File

@ -3,36 +3,29 @@ package ru.dbotthepony.kommons.io
import ru.dbotthepony.kommons.util.KOptional import ru.dbotthepony.kommons.util.KOptional
import java.io.Closeable import java.io.Closeable
import java.io.File import java.io.File
import java.util.concurrent.CompletableFuture
/** /**
* For actual implementation, see [BTreeDB6]. * For actual implementation, see [BTreeDB6].
*
*
*/ */
abstract class BTreeDB<K, V> : Closeable { abstract class BTreeDB<K, V> : Closeable {
abstract val file: File abstract val file: File
abstract val blockSize: Int abstract val blockSize: Int
open operator fun contains(key: K): Boolean { abstract operator fun contains(key: K): Boolean
return hasKey(key).get()
}
abstract fun hasKey(key: K): CompletableFuture<Boolean>
/** /**
* Reads data at specified [key] * Reads data at specified [key]
*/ */
abstract fun read(key: K): CompletableFuture<KOptional<V>> abstract fun read(key: K): KOptional<V>
abstract fun findAllKeys(): CompletableFuture<List<K>> abstract fun findAllKeys(): List<K>
abstract fun write(key: K, value: V): CompletableFuture<*> abstract fun write(key: K, value: V)
} }
abstract class ByteDataBTreeDB<K> : BTreeDB<K, ByteArray>() { abstract class ByteDataBTreeDB<K> : BTreeDB<K, ByteArray>() {
abstract fun write(key: K, value: ByteArray, offset: Int, length: Int): CompletableFuture<*> abstract fun write(key: K, value: ByteArray, offset: Int, length: Int)
final override fun write(key: K, value: ByteArray): CompletableFuture<*> { final override fun write(key: K, value: ByteArray) {
return write(key, value, 0, value.size) return write(key, value, 0, value.size)
} }
} }

View File

@ -46,10 +46,8 @@ private fun readHeader(reader: RandomAccessFile, required: Char) {
* This storage does not employ any form of defragmentation, but writes try to allocate * This storage does not employ any form of defragmentation, but writes try to allocate
* (reasonably sized) continuous blocks, to avoid severe internal fragmentation. * (reasonably sized) continuous blocks, to avoid severe internal fragmentation.
* *
* Specified [Executor] is utilized to execute actual I/O operations.
*
* [sync] determines whenever writes should be performed atomically. If [sync] is false, in event of * [sync] determines whenever writes should be performed atomically. If [sync] is false, in event of
* JVM crash, host system crash or power loss, there is a risk of file becoming unreadable, unless underlying filesystem * host system crash or power loss, there is a risk of file becoming unreadable, unless underlying filesystem
* and/or device ensures writes to file are performed in the same order as they were issued by code. * and/or device ensures writes to file are performed in the same order as they were issued by code.
* If performance is priority, specifying sync as false will yield vastly better performance for writes. * If performance is priority, specifying sync as false will yield vastly better performance for writes.
* *
@ -65,15 +63,13 @@ private fun readHeader(reader: RandomAccessFile, required: Char) {
// TODO: Changeset counter (to determine write-in-progress blocks/trees, to ignore them when reconstructing tree) // TODO: Changeset counter (to determine write-in-progress blocks/trees, to ignore them when reconstructing tree)
// TODO: Tree rotations (rebalancing) // TODO: Tree rotations (rebalancing)
// TODO: Removal of keys // TODO: Removal of keys
class BTreeDB6 private constructor(override val file: File, private var reader: RandomAccessFile, pool: Executor, private val sync: Boolean) : ByteDataBTreeDB<ByteKey>() { class BTreeDB6 private constructor(override val file: File, private var reader: RandomAccessFile, private val sync: Boolean) : ByteDataBTreeDB<ByteKey>() {
constructor(file: File, pool: Executor, sync: Boolean = true) : this(file, RandomAccessFile(file, "rw"), pool, sync) constructor(file: File, sync: Boolean = true) : this(file, RandomAccessFile(file, "rw"), sync)
init { init {
reader.seek(0L) reader.seek(0L)
} }
private val carrier = CarriedExecutor(pool)
init { init {
readHeader(reader, 'B') readHeader(reader, 'B')
readHeader(reader, 'T') readHeader(reader, 'T')
@ -86,9 +82,7 @@ class BTreeDB6 private constructor(override val file: File, private var reader:
} }
override fun close() { override fun close() {
carrier.execute { reader.close() } reader.close()
carrier.shutdown()
carrier.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS)
} }
override val blockSize = reader.readInt() override val blockSize = reader.readInt()
@ -229,7 +223,11 @@ class BTreeDB6 private constructor(override val file: File, private var reader:
} }
} }
private fun doRead(key: ByteKey): KOptional<ByteArray> { override fun contains(key: ByteKey): Boolean {
return searchForBlock(key) != null
}
override fun read(key: ByteKey): KOptional<ByteArray> {
val data = searchForBlock(key) val data = searchForBlock(key)
if (data != null) { if (data != null) {
@ -242,18 +240,6 @@ class BTreeDB6 private constructor(override val file: File, private var reader:
return KOptional.empty() return KOptional.empty()
} }
override fun hasKey(key: ByteKey): CompletableFuture<Boolean> {
return CompletableFuture.supplyAsync(Supplier {
searchForBlock(key) != null
}, carrier)
}
override fun read(key: ByteKey): CompletableFuture<KOptional<ByteArray>> {
return CompletableFuture.supplyAsync(Supplier {
doRead(key)
}, carrier)
}
private fun readKeysInto(block: Block, result: MutableList<ByteKey>) { private fun readKeysInto(block: Block, result: MutableList<ByteKey>) {
when (block.type) { when (block.type) {
BlockType.TREE -> { BlockType.TREE -> {
@ -276,7 +262,7 @@ class BTreeDB6 private constructor(override val file: File, private var reader:
} }
} }
private fun doFindAllKeys(): List<ByteKey> { override fun findAllKeys(): List<ByteKey> {
if (rootBlockIndex == INVALID_BLOCK_INDEX) { if (rootBlockIndex == INVALID_BLOCK_INDEX) {
return emptyList() return emptyList()
} }
@ -286,13 +272,7 @@ class BTreeDB6 private constructor(override val file: File, private var reader:
return result return result
} }
override fun findAllKeys(): CompletableFuture<List<ByteKey>> { override fun write(key: ByteKey, value: ByteArray, offset: Int, length: Int) {
return CompletableFuture.supplyAsync(Supplier {
doFindAllKeys()
}, carrier)
}
private fun doWriteData(key: ByteKey, data: ByteArray, offset: Int, length: Int) {
if (rootBlockIndex == INVALID_BLOCK_INDEX) { if (rootBlockIndex == INVALID_BLOCK_INDEX) {
// create LEAF node for root // create LEAF node for root
val block = allocBlock(BlockType.LEAF) val block = allocBlock(BlockType.LEAF)
@ -308,7 +288,7 @@ class BTreeDB6 private constructor(override val file: File, private var reader:
val stream = DataOutputStream(BlockOutputStream(newData)) val stream = DataOutputStream(BlockOutputStream(newData))
stream.writeInt(length) stream.writeInt(length)
stream.write(data, offset, length) stream.write(value, offset, length)
stream.close() stream.close()
rootBlockIndex = leaf.write() rootBlockIndex = leaf.write()
@ -346,7 +326,7 @@ class BTreeDB6 private constructor(override val file: File, private var reader:
val leaf = LeafData(currentBlock) val leaf = LeafData(currentBlock)
val stream = DataOutputStream(BlockOutputStream(leaf.compute(key, length + 4))) val stream = DataOutputStream(BlockOutputStream(leaf.compute(key, length + 4)))
stream.writeInt(length) stream.writeInt(length)
stream.write(data, offset, length) stream.write(value, offset, length)
stream.close() stream.close()
if (leaf.shouldSplit) { if (leaf.shouldSplit) {
@ -415,12 +395,6 @@ class BTreeDB6 private constructor(override val file: File, private var reader:
} }
} }
override fun write(key: ByteKey, value: ByteArray, offset: Int, length: Int): CompletableFuture<Void> {
return CompletableFuture.runAsync(Runnable {
doWriteData(key, value, offset, length)
}, carrier)
}
private fun allocBlock(type: BlockType): Block { private fun allocBlock(type: BlockType): Block {
return allocBlocks(type, 1)[0] return allocBlocks(type, 1)[0]
} }
@ -1023,7 +997,7 @@ class BTreeDB6 private constructor(override val file: File, private var reader:
*/ */
@JvmStatic @JvmStatic
@JvmOverloads @JvmOverloads
fun create(file: File, pool: Executor, blockSize: Int = 4096, sync: Boolean = true): BTreeDB6 { fun create(file: File, blockSize: Int = 4096, sync: Boolean = true): BTreeDB6 {
require(blockSize >= 128) { "Degenerate block size: $blockSize" } require(blockSize >= 128) { "Degenerate block size: $blockSize" }
if (file.exists()) if (file.exists())
@ -1040,7 +1014,7 @@ class BTreeDB6 private constructor(override val file: File, private var reader:
writer.writeInt(INVALID_BLOCK_INDEX) // free bitmap block index writer.writeInt(INVALID_BLOCK_INDEX) // free bitmap block index
writer.writeInt(INVALID_BLOCK_INDEX) // tree root block index writer.writeInt(INVALID_BLOCK_INDEX) // tree root block index
return BTreeDB6(file, writer, pool, sync) return BTreeDB6(file, writer, sync)
} }
} }
} }

View File

@ -14,14 +14,19 @@ object BTreeDB6Tests {
fun test() { fun test() {
val file = File("dbtest.bdb") val file = File("dbtest.bdb")
if (file.exists()) file.delete() if (file.exists()) file.delete()
val create = BTreeDB6.create(file, Executor { it.run() }, 4096, sync = false) val create = BTreeDB6.create(file, 4096, sync = false)
for (i in 0 .. 8000) { for (i in 0 .. 8000) {
val s = "This is key $i" val s = "This is key $i"
val k = ByteKey("This is key $i") val k = ByteKey("This is key $i")
println("This is key $i")
create.write(k, s.toByteArray()) create.write(k, s.toByteArray())
assertEquals(s, String(create.read(k).get().get())) assertEquals(s, String(create.read(k).get()))
}
for (i in 0 .. 8000) {
val s = "This is key $i"
val k = ByteKey("This is key $i")
assertEquals(s, String(create.read(k).get()))
} }
create.close() create.close()