Skip to content

Commit

Permalink
Replace WeakHashMap by LimitedPersistentMap in IndexDb and move most …
Browse files Browse the repository at this point in the history
…of qbit code into qbit-core
  • Loading branch information
Aleksey Zhidkov committed May 30, 2020
1 parent 1fd7eb0 commit 2306e7f
Show file tree
Hide file tree
Showing 41 changed files with 324 additions and 156 deletions.
10 changes: 2 additions & 8 deletions qbit-core-jvm/src/commonMain/kotlin/qbit/BootstrapJvm.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,15 @@ import kotlinx.serialization.modules.SerialModule
import qbit.api.Attrs
import qbit.api.Instances
import qbit.api.db.Conn
import qbit.api.model.Attr
import qbit.api.model.Eav
import qbit.api.protoInstance
import qbit.api.system.DbUuid
import qbit.api.tombstone
import qbit.factoring.Factor
import qbit.ns.Namespace
import qbit.platform.collections.EmptyIterator
import qbit.platform.currentTimeMillis
import qbit.serialization.JvmNodesStorage
import qbit.serialization.NodeData
import qbit.serialization.NodesStorage
import qbit.serialization.Root
import qbit.spi.Storage
import qbit.storage.SerializedStorage
Expand All @@ -26,12 +24,8 @@ internal suspend fun bootstrap(storage: Storage, dbUuid: DbUuid, factor: Factor,
.plus(factor(protoInstance, bootstrapSchema::get, EmptyIterator))

val root = Root(null, dbUuid, currentTimeMillis(), NodeData(trx.toTypedArray()))
val storedRoot = NodesStorage(serializedStorage).store(root)
val storedRoot = JvmNodesStorage(serializedStorage).store(root)
serializedStorage.add(Namespace("refs")["head"], storedRoot.hash.bytes)
return QConn(serialModule, dbUuid, serializedStorage, storedRoot, factor)
}

internal fun Attr<*>.toFacts(): List<Eav> = listOf(Eav(this.id!!, Attrs.name.name, this.name),
Eav(this.id!!, Attrs.type.name, this.type),
Eav(this.id!!, Attrs.unique.name, this.unique),
Eav(this.id!!, Attrs.list.name, this.list))
17 changes: 5 additions & 12 deletions qbit-core-jvm/src/commonMain/kotlin/qbit/Conn.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,7 @@ import kotlinx.serialization.modules.SerialModule
import kotlinx.serialization.modules.SerialModuleCollector
import kotlinx.serialization.modules.plus
import qbit.api.QBitException
import qbit.api.db.Conn
import qbit.api.db.Db
import qbit.api.db.Trx
import qbit.api.db.WriteResult
import qbit.api.db.pull
import qbit.api.db.*
import qbit.api.gid.Gid
import qbit.api.gid.Iid
import qbit.api.model.Hash
Expand All @@ -23,10 +19,7 @@ import qbit.factoring.serializatoin.KSFactorizer
import qbit.index.Indexer
import qbit.index.InternalDb
import qbit.ns.Namespace
import qbit.serialization.Node
import qbit.serialization.NodeRef
import qbit.serialization.NodeVal
import qbit.serialization.NodesStorage
import qbit.serialization.*
import qbit.spi.Storage
import qbit.trx.CommitHandler
import qbit.trx.QTrx
Expand Down Expand Up @@ -76,7 +69,7 @@ suspend fun qbit(storage: Storage, appSerialModule: SerialModule): Conn {
val systemSerialModule = qbitSerialModule + appSerialModule
systemSerialModule.dumpTo(SchemaValidator())
return if (headHash != null) {
val head = NodesStorage(storage).load(NodeRef(Hash(headHash)))
val head = JvmNodesStorage(storage).load(NodeRef(Hash(headHash)))
?: throw QBitException("Corrupted head: no such node")
// TODO: fix dbUuid retrieving
QConn(systemSerialModule, dbUuid, storage, head, KSFactorizer(systemSerialModule)::factor)
Expand All @@ -85,9 +78,9 @@ suspend fun qbit(storage: Storage, appSerialModule: SerialModule): Conn {
}
}

internal class QConn(serialModule: SerialModule, override val dbUuid: DbUuid, val storage: Storage, head: NodeVal<Hash>, private val factor: Factor) : Conn(), CommitHandler {
class QConn(serialModule: SerialModule, override val dbUuid: DbUuid, val storage: Storage, head: NodeVal<Hash>, private val factor: Factor) : Conn(), CommitHandler {

private val nodesStorage = NodesStorage(storage)
private val nodesStorage = JvmNodesStorage(storage)

var trxLog: TrxLog = QTrxLog(head, Writer(nodesStorage, dbUuid))

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,25 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.withContext
import qbit.api.QBitException
import qbit.api.model.Hash
import qbit.api.model.hash
import qbit.ns.Namespace
import qbit.platform.MessageDigests
import qbit.platform.asInput
import qbit.platform.createSingleThreadCoroutineDispatcher
import qbit.spi.Storage

private val nodes = Namespace("nodes")

class NodesStorage(private val storage: Storage) :
class JvmNodesStorage(private val storage: Storage) :
(NodeRef) -> NodeVal<Hash>?,
CoroutineScope by CoroutineScope(createSingleThreadCoroutineDispatcher("Nodes writer")) {
CoroutineScope by CoroutineScope(createSingleThreadCoroutineDispatcher("Nodes writer")),
NodesStorage {

suspend fun store(n: NodeVal<Hash?>): NodeVal<Hash> {
override suspend fun store(n: NodeVal<Hash?>): NodeVal<Hash> {
return withContext(this.coroutineContext) {
val data = SimpleSerialization.serializeNode(n)
val hash = hash(data)
if (n.hash != null && n.hash != hash) {
throw AssertionError("NodeVal has hash ${n.hash.toHexString()}, but it's serialization has hash ${hash.toHexString()}")
throw AssertionError("NodeVal has hash ${n.hash!!.toHexString()}, but it's serialization has hash ${hash.toHexString()}")
}
if (!storage.hasKey(hash.key())) {
storage.add(hash.key(), data)
Expand All @@ -43,7 +44,7 @@ class NodesStorage(private val storage: Storage) :
}
}

fun load(n: NodeRef): NodeVal<Hash>? {
override fun load(n: NodeRef): NodeVal<Hash>? {
return invoke(n)
}

Expand All @@ -57,6 +58,10 @@ class NodesStorage(private val storage: Storage) :
is Merge -> Merge(hash, n.parent1, n.parent2, n.source, n.timestamp, n.data)
}

fun hasNode(head: Node<Hash>): Boolean =
override fun hasNode(head: Node<Hash>): Boolean =
storage.hasKey(head.key())

}

fun hash(data: ByteArray): Hash =
Hash(MessageDigests.getInstance("SHA-1").digest(data))
7 changes: 6 additions & 1 deletion qbit-core-jvm/src/commonTest/kotlin/qbit/BootstrapTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,12 @@ class BootstrapTest {
private val newDb: Conn

init {
newDb = runBlocking { bootstrap(storage, DbUuid(Iid(1, 4)), testSchemaFactorizer::factor, qbitSerialModule + testsSerialModule) }
newDb = runBlocking {
bootstrap(
storage,
DbUuid(Iid(1, 4)), testSchemaFactorizer::factor, qbitSerialModule + testsSerialModule
)
}
}

@Test
Expand Down
7 changes: 2 additions & 5 deletions qbit-core-jvm/src/commonTest/kotlin/qbit/ConnTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,7 @@ import qbit.api.system.DbUuid
import qbit.ns.Namespace
import qbit.platform.currentTimeMillis
import qbit.platform.runBlocking
import qbit.serialization.Leaf
import qbit.serialization.NodeData
import qbit.serialization.NodesStorage
import qbit.serialization.Root
import qbit.serialization.*
import qbit.storage.MemStorage
import qbit.test.model.IntEntity
import qbit.test.model.testsSerialModule
Expand All @@ -24,7 +21,7 @@ class ConnTest {
runBlocking {
val storage = MemStorage()
val dbUuid = DbUuid(Iid(0, 4))
val nodesStorage = NodesStorage(storage)
val nodesStorage = JvmNodesStorage(storage)

val root = Root(null, dbUuid, currentTimeMillis(), NodeData(emptyArray()))
val storedRoot = nodesStorage.store(root)
Expand Down
4 changes: 2 additions & 2 deletions qbit-core-jvm/src/commonTest/kotlin/qbit/FunTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import qbit.api.db.*
import qbit.api.gid.Gid
import qbit.index.InternalDb
import qbit.platform.runBlocking
import qbit.serialization.JvmNodesStorage
import qbit.serialization.NodeRef
import qbit.serialization.NodesStorage
import qbit.storage.MemStorage
import qbit.test.model.*
import kotlin.test.*
Expand Down Expand Up @@ -125,7 +125,7 @@ class FunTest {

assertEquals(
5,
NodesStorage(storage).load(NodeRef(conn.head))!!.data.trxes.size,
JvmNodesStorage(storage).load(NodeRef(conn.head))!!.data.trxes.size,
"5 facts (2 for region and 3 for instance) expected"
)
}
Expand Down
30 changes: 2 additions & 28 deletions qbit-core-jvm/src/commonTest/kotlin/qbit/TestUtilsJvm.kt
Original file line number Diff line number Diff line change
@@ -1,24 +1,19 @@
package qbit

import kotlinx.serialization.modules.SerialModule
import qbit.api.QBitException
import qbit.api.db.Fetch
import qbit.api.db.QueryPred
import qbit.api.gid.Gid
import qbit.api.gid.nextGids
import qbit.api.model.*
import qbit.api.model.impl.DetachedEntity
import qbit.api.model.impl.QStoredEntity
import qbit.index.Index
import qbit.index.IndexDb
import qbit.index.InternalDb
import qbit.api.model.impl.DetachedEntity
import qbit.api.model.impl.QStoredEntity
import qbit.index.Indexer
import qbit.serialization.Node
import qbit.serialization.NodeVal
import qbit.test.model.testsSerialModule
import kotlin.reflect.KClass
import kotlin.test.assertEquals
import kotlin.test.fail

internal fun dbOf(eids: Iterator<Gid> = Gid(0, 0).nextGids(), vararg entities: Any): InternalDb {
val addedAttrs = entities
Expand Down Expand Up @@ -47,10 +42,6 @@ internal object EmptyDb : InternalDb() {

}

val identityNodeResolver: (Node<Hash>) -> NodeVal<Hash>? = { it as? NodeVal<Hash> }

fun mapNodeResolver(map: Map<Hash, NodeVal<Hash>>): (Node<Hash>) -> NodeVal<Hash>? = { n -> map[n.hash] }

inline fun <reified T : Any> Attr(name: String, unique: Boolean = true): Attr<T> =
Attr(null, name, unique)

Expand Down Expand Up @@ -106,20 +97,3 @@ fun assertArrayEquals(arr1: ByteArray?, arr2: ByteArray?) {
(arr1 zip arr2).forEach { assertEquals(it.first, it.second) }
}

inline fun <reified E : Throwable> assertThrows(body: () -> Unit) {
try {
body()
fail("${E::class} exception expected")
} catch (e : Throwable) {
if (e !is E) {
throw e
}
}
}

internal fun TestIndexer(
serialModule: SerialModule = testsSerialModule,
baseDb: IndexDb? = null,
baseHash: Hash? = null,
nodeResolver: (Node<Hash>) -> NodeVal<Hash>? = identityNodeResolver) =
Indexer(serialModule, baseDb, baseHash, nodeResolver)
8 changes: 0 additions & 8 deletions qbit-core-jvm/src/jvmMain/kotlin/qbit/platform/Collections.kt

This file was deleted.

20 changes: 19 additions & 1 deletion qbit-core/src/commonMain/kotlin/qbit/Bootstrap.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@ import kotlinx.serialization.SerialDescriptor
import kotlinx.serialization.modules.SerializersModule
import qbit.api.Attrs
import qbit.api.Instances
import qbit.api.gid.Gid
import qbit.api.gid.Iid
import qbit.api.model.Attr
import qbit.api.model.Eav
import qbit.api.model.QBoolean
import qbit.api.model.impl.QTombstone
import qbit.api.system.Instance
import qbit.api.tombstone


val qbitSerialModule = SerializersModule {
Expand All @@ -19,6 +22,14 @@ val qbitSerialModule = SerializersModule {
contextual(QTombstone::class, QTombstone.serializer())
}

val tombstone = Attr<Boolean>(
Gid(Iid(1, 4), 7),
"qbit.api/tombstone",
QBoolean.code,
unique = false,
list = false
)

val bootstrapSchema: Map<String, Attr<Any>> = mapOf(
(Attrs.name.name to Attrs.name) as Pair<String, Attr<Any>>,
(Attrs.type.name to Attrs.type) as Pair<String, Attr<Any>>,
Expand All @@ -44,3 +55,10 @@ private class FakeSerializer<T> : KSerializer<T> {
}

}

fun Attr<*>.toFacts(): List<Eav> = listOf(
Eav(this.id!!, Attrs.name.name, this.name),
Eav(this.id!!, Attrs.type.name, this.type),
Eav(this.id!!, Attrs.unique.name, this.unique),
Eav(this.id!!, Attrs.list.name, this.list)
)
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package qbit.api.db
import qbit.api.model.Hash
import qbit.api.system.DbUuid

abstract class Conn internal constructor() {
abstract class Conn() {

abstract val dbUuid: DbUuid

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
package qbit.api.model

import qbit.platform.MessageDigests

const val HASH_LEN = 20

val nullHash = Hash(ByteArray(HASH_LEN))

fun hash(data: ByteArray): Hash = Hash(MessageDigests.getInstance("SHA-1").digest(data))

data class Hash(val bytes: ByteArray) {

override fun equals(other: Any?): Boolean {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package qbit.collections


class LimitedPersistentMap<K : Any, V : Any>(
private val limit: Int,
private val delegate: PersistentMap<K, V> = PersistentMapStub()
) : PersistentMap<K, V> by delegate {

override fun put(key: K, value: V): LimitedPersistentMap<K, V> {
val limited = if (delegate.size < limit) {
this.delegate
} else {
this.delegate.remove(this.keys.first())
}
return LimitedPersistentMap(limit, limited.put(key, value))
}

}
34 changes: 3 additions & 31 deletions qbit-core/src/commonMain/kotlin/qbit/collections/PersistentMap.kt
Original file line number Diff line number Diff line change
@@ -1,38 +1,10 @@
package qbit.collections

import kotlinx.atomicfu.AtomicRef
import kotlinx.atomicfu.atomic
import kotlinx.atomicfu.updateAndGet

interface PersistentMap<K : Any, V : Any> : Map<K, V> {

/**
* Dump persistent map as stub for real persistent map from kotlinx-immutable while Kotlin 1.4 stabiliaztion
*/
class PersistentMap<K : Any, V : Any> {

private val mapRef: AtomicRef<Map<K, V>> = atomic(HashMap())

val keys: Set<K>
get() = mapRef.value.keys

fun put(key: K, value: V): PersistentMap<K, V> {
mapRef.updateAndGet {
it.plus(key to value)
}
return this
}

operator fun get(key: K): V? {
return mapRef.value[key]
}

fun containsKey(key: K): Boolean {
return mapRef.value.containsKey(key)
}

operator fun contains(key: K): Boolean {
return containsKey(key)
}
fun put(key: K, value: V): PersistentMap<K, V>

fun remove(key: K): PersistentMap<K, V>

}
Loading

0 comments on commit 2306e7f

Please sign in to comment.