Skip to content

Commit 899f7a9

Browse files
committed
refactor: removed prefetching
1 parent 342a84e commit 899f7a9

File tree

14 files changed

+24
-348
lines changed

14 files changed

+24
-348
lines changed

model-client/src/commonMain/kotlin/org/modelix/model/ModelMigrations.kt

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ import org.modelix.model.api.ITree
55
import org.modelix.model.api.IWriteTransaction
66
import org.modelix.model.area.IArea
77
import org.modelix.model.area.PArea
8-
import org.modelix.model.lazy.CLTree
9-
import org.modelix.model.lazy.PrefetchCache
108
import org.modelix.model.lazy.unwrap
119
import org.modelix.model.metameta.MetaModelMigration
1210

@@ -15,10 +13,7 @@ object ModelMigrations {
1513
fun useCanonicalReferences(branch: IBranch) {
1614
branch.runWriteT { t ->
1715
val tree = t.tree.unwrap()
18-
PrefetchCache.with(tree) {
19-
(tree as? CLTree)?.prefetchAll()
20-
useCanonicalReferences(t, PArea(branch), ITree.ROOT_ID)
21-
}
16+
useCanonicalReferences(t, PArea(branch), ITree.ROOT_ID)
2217
}
2318
}
2419

model-client/src/jvmMain/kotlin/org/modelix/model/client/RestWebModelClient.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ import org.modelix.model.KeyValueStoreCache
4646
import org.modelix.model.api.IIdGenerator
4747
import org.modelix.model.lazy.IDeserializingKeyValueStore
4848
import org.modelix.model.lazy.ObjectStoreCache
49-
import org.modelix.model.lazy.PrefetchCache
5049
import org.modelix.model.oauth.ModelixAuthClient
5150
import org.modelix.model.persistent.HashUtil
5251
import org.modelix.model.sleep
@@ -209,7 +208,7 @@ class RestWebModelClient @JvmOverloads constructor(
209208
}
210209
private val listeners: MutableList<PollingListener> = ArrayList()
211210
override val asyncStore: IKeyValueStore = AsyncStore(this)
212-
private val cache = PrefetchCache.contextIndirectCache(ObjectStoreCache(KeyValueStoreCache(asyncStore)))
211+
private val cache = ObjectStoreCache(KeyValueStoreCache(asyncStore))
213212
private val pendingWrites = AtomicInteger(0)
214213
var connectionStatus: ConnectionStatus = ConnectionStatus.NEW
215214
private set(value) {

model-datastructure/src/commonMain/kotlin/org/modelix/model/async/AsyncStoreAsLegacyDeserializingStore.kt

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,7 @@ class AsyncStoreAsLegacyDeserializingStore(val store: IAsyncObjectStore) : IDese
3838

3939
override fun <T : IKVValue> getAll(
4040
regular: List<IKVEntryReference<T>>,
41-
prefetch: List<IKVEntryReference<T>>,
4241
): Map<String, T?> {
43-
return store.getAllAsMap((regular + prefetch).map { it.toObjectHash() }).getSynchronous().entries.associate { it.key.hash to it.value as T? }
44-
}
45-
46-
override fun prefetch(hash: String) {
47-
throw UnsupportedOperationException("prefetch is deprecated")
42+
return store.getAllAsMap(regular.map { it.toObjectHash() }).getSynchronous().entries.associate { it.key.hash to it.value as T? }
4843
}
4944
}

model-datastructure/src/commonMain/kotlin/org/modelix/model/async/AsyncTree.kt

Lines changed: 1 addition & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,6 @@ import org.modelix.model.api.async.getDescendantsAndSelf
2727
import org.modelix.model.api.meta.NullConcept
2828
import org.modelix.model.lazy.CLTree
2929
import org.modelix.model.lazy.COWArrays.insert
30-
import org.modelix.model.lazy.IBulkQuery
31-
import org.modelix.model.lazy.IPrefetchGoal
3230
import org.modelix.model.lazy.KVEntryReference
3331
import org.modelix.model.lazy.NodeNotFoundException
3432
import org.modelix.model.persistent.CPHamtInternal
@@ -76,34 +74,6 @@ open class AsyncTree(val treeData: CPTree, val store: IAsyncObjectStore) : IAsyn
7674

7775
private fun tryGetNodeRef(id: Long): IStream.ZeroOrOne<KVEntryReference<CPNode>> = nodesMap.query().flatMapZeroOrOne { it.get(id, store) }
7876

79-
@Deprecated("Prefetching will be replaced by usages of IAsyncNode")
80-
private fun loadPrefetch(node: CPNode) {
81-
val bulkQuery = (store as? BulkQueryAsAsyncStore)?.bulkQuery
82-
if (bulkQuery != null) {
83-
val children: LongArray = node.childrenIdArray
84-
if (children.isNotEmpty()) {
85-
children.reversedArray().forEach {
86-
bulkQuery.offerPrefetch(PrefetchNodeGoal(this, it))
87-
}
88-
}
89-
if (node.parentId != 0L) {
90-
bulkQuery.offerPrefetch(PrefetchNodeGoal(this, node.parentId))
91-
}
92-
node.referenceTargets.asSequence().filter { it.isLocal }.forEach { target ->
93-
bulkQuery.offerPrefetch(PrefetchNodeGoal(this, target.elementId))
94-
}
95-
}
96-
}
97-
98-
@Deprecated("Prefetching will be replaced by usages of IAsyncNode")
99-
private fun IStream.Many<Long>.loadPrefetch(): IStream.Many<Long> {
100-
val bulkQuery = (store as? BulkQueryAsAsyncStore)?.bulkQuery ?: return this
101-
return map {
102-
bulkQuery.offerPrefetch(PrefetchNodeGoal(this@AsyncTree, it))
103-
it
104-
}
105-
}
106-
10777
override fun asSynchronousTree(): ITree {
10878
return CLTree(treeData, store)
10979
// return AsyncAsSynchronousTree(this)
@@ -260,7 +230,7 @@ open class AsyncTree(val treeData: CPTree, val store: IAsyncObjectStore) : IAsyn
260230
}
261231

262232
override fun getAllChildren(parentId: Long): IStream.Many<Long> {
263-
return getNode(parentId).flatMapIterable { it.childrenIdArray.toList() }.loadPrefetch()
233+
return getNode(parentId).flatMapIterable { it.childrenIdArray.toList() }
264234
}
265235

266236
override fun getAllReferenceTargetRefs(sourceId: Long): IStream.Many<Pair<IReferenceLinkReference, INodeReference>> {
@@ -283,7 +253,6 @@ open class AsyncTree(val treeData: CPTree, val store: IAsyncObjectStore) : IAsyn
283253
.flatMap { getNodes(it.childrenIdArray) }
284254
.filter { it.roleInParent == roleString }
285255
.map { it.id }
286-
.loadPrefetch()
287256
}
288257

289258
private fun IRoleReference.key() = getRoleKey(this)
@@ -509,17 +478,6 @@ open class AsyncTree(val treeData: CPTree, val store: IAsyncObjectStore) : IAsyn
509478
}
510479
}
511480

512-
@Deprecated("Prefetching will be replaced by usages of IAsyncNode")
513-
private data class PrefetchNodeGoal(val tree: AsyncTree, val nodeId: Long) : IPrefetchGoal {
514-
override fun loadRequest(bulkQuery: IBulkQuery): IStream.Many<Any?> {
515-
return tree.getAllChildren(nodeId).flatMap { tree.getNode(it) }
516-
}
517-
518-
override fun toString(): String {
519-
return nodeId.toString(16)
520-
}
521-
}
522-
523481
class ContainmentCycleException(val newParentId: Long, val childId: Long) :
524482
RuntimeException(
525483
"${newParentId.toString(16)} is a descendant of ${childId.toString(16)}." +

model-datastructure/src/commonMain/kotlin/org/modelix/model/async/LegacyDeserializingStoreAsAsyncStore.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,12 @@ class LegacyDeserializingStoreAsAsyncStore(val store: IDeserializingKeyValueStor
2424
}
2525

2626
override fun getAllAsStream(keys: IStream.Many<ObjectHash<*>>): IStream.Many<Pair<ObjectHash<*>, Any?>> {
27-
val entries = store.getAll(keys.map { it.toKVEntryReference<IKVValue>() }.toList().getSynchronous(), emptyList())
27+
val entries = store.getAll(keys.map { it.toKVEntryReference<IKVValue>() }.toList().getSynchronous())
2828
return keys.map { it to entries[it.hash] }
2929
}
3030

3131
override fun getAllAsMap(keys: List<ObjectHash<*>>): IStream.One<Map<ObjectHash<*>, Any?>> {
32-
val entries = store.getAll(keys.map { it.toKVEntryReference() }, emptyList())
32+
val entries = store.getAll(keys.map { it.toKVEntryReference() })
3333
return IStream.of(keys.associate { it to entries[it.hash] })
3434
}
3535

model-datastructure/src/commonMain/kotlin/org/modelix/model/lazy/BulkQuery.kt

Lines changed: 13 additions & 136 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package org.modelix.model.lazy
22

33
import com.badoo.reaktive.single.notNull
44
import kotlinx.coroutines.sync.Mutex
5-
import kotlinx.coroutines.sync.withLock
65
import org.modelix.kotlin.utils.AtomicBoolean
76
import org.modelix.model.persistent.IKVValue
87
import org.modelix.streams.CompletableObservable
@@ -17,63 +16,38 @@ class BulkQuery(private val store: IDeserializingKeyValueStore, config: BulkQuer
1716
private val queue: MutableMap<String, QueueElement<out IKVValue>> = LinkedHashMap()
1817
private var processing = AtomicBoolean(false)
1918
private val batchSize: Int = config.requestBatchSize
20-
private val prefetchSize: Int = config.prefetchBatchSize ?: (this.batchSize / 2)
21-
private val prefetchQueueSizeLimit: Int = (this.prefetchSize * 10).coerceAtLeast(this.batchSize * 2)
22-
private val prefetchQueue: PrefetchQueue = PrefetchQueue(this, prefetchQueueSizeLimit).also {
23-
it.requestFilter = { !queue.contains(it.getHash()) }
24-
}
2519
private val processingMutex = Mutex()
2620

27-
init {
28-
require(this.prefetchSize <= this.batchSize) { "prefetch size ${this.prefetchSize} is greater than the batch size ${this.batchSize}" }
29-
}
30-
3121
private fun <T : IKVValue> getValueInstance(ref: IKVEntryReference<T>): CompletableObservable<T?>? {
3222
return queue[ref.getHash()]?.let { it.value as CompletableObservable<T?>? }
33-
?: (prefetchQueue.getValueInstance(ref) as CompletableObservable<T?>?)
3423
}
3524

36-
private fun executeBulkQuery(regular: List<IKVEntryReference<IKVValue>>, prefetch: List<IKVEntryReference<IKVValue>>): Map<String, IKVValue?> {
37-
return store.getAll(regular, prefetch)
25+
private fun executeBulkQuery(regular: List<IKVEntryReference<IKVValue>>): Map<String, IKVValue?> {
26+
return store.getAll(regular)
3827
}
3928

4029
override fun <T : IKVValue> query(hash: IKVEntryReference<T>): IStream.ZeroOrOne<T> {
4130
if (!hash.isWritten()) return IStream.of(hash.getValue(store))
4231

43-
val cachedValue = store.getIfCached(hash.getHash(), hash.getDeserializer(), prefetchQueue.isLoadingGoal())
32+
val cachedValue = store.getIfCached(hash.getHash(), hash.getDeserializer(), false)
4433
if (cachedValue != null) {
4534
return IStream.of(cachedValue)
4635
}
4736

4837
val existingValue = getValueInstance(hash)
4938
if (existingValue != null && existingValue.isDone()) return ReaktiveStreamBuilder.WrapperMaybe(existingValue.single.notNull())
5039

51-
if (prefetchQueue.isLoadingGoal()) {
52-
prefetchQueue.addRequest(hash, getValueInstance(hash) ?: CompletableObservable(::executeQuery))
53-
return IStream.empty() // transitive objects are loaded when the prefetch queue is processed the next time
54-
} else {
55-
if (queue.size >= batchSize && !processing.get()) executeQuery()
40+
if (queue.size >= batchSize && !processing.get()) executeQuery()
5641

57-
val existingQueueElement = queue[hash.getHash()] as QueueElement<T>?
58-
val result = if (existingQueueElement != null) {
59-
existingQueueElement.value
60-
} else {
61-
val result: CompletableObservable<T?> = getValueInstance(hash) ?: CompletableObservable(::executeQuery)
62-
queue.put(hash.getHash(), QueueElement<T>(hash, result))
63-
result
64-
}
65-
return ReaktiveStreamBuilder.WrapperMaybe(result.single.notNull())
66-
}
67-
}
68-
69-
override fun offerPrefetch(goal: IPrefetchGoal) {
70-
prefetchQueue.addGoal(goal)
71-
}
72-
73-
private suspend fun executeQuerySuspending() {
74-
processingMutex.withLock {
75-
executeQuery()
42+
val existingQueueElement = queue[hash.getHash()] as QueueElement<T>?
43+
val result = if (existingQueueElement != null) {
44+
existingQueueElement.value
45+
} else {
46+
val result: CompletableObservable<T?> = getValueInstance(hash) ?: CompletableObservable(::executeQuery)
47+
queue.put(hash.getHash(), QueueElement<T>(hash, result))
48+
result
7649
}
50+
return ReaktiveStreamBuilder.WrapperMaybe(result.single.notNull())
7751
}
7852

7953
override fun executeQuery() {
@@ -86,17 +60,12 @@ class BulkQuery(private val store: IDeserializingKeyValueStore, config: BulkQuer
8660
val regularRequests: List<Pair<IKVEntryReference<*>, CompletableObservable<*>>> = queue.entries.tailSequence(batchSize)
8761
.map { it.value.hash to it.value.value }
8862
.toList()
89-
if (queue.size < prefetchSize) {
90-
prefetchQueue.fillRequestsQueue(prefetchSize - regularRequests.size)
91-
}
92-
val prefetchRequests: List<Pair<IKVEntryReference<*>, CompletableObservable<*>>> = prefetchQueue.getRequests(prefetchSize - regularRequests.size)
9363
regularRequests.forEach { queue.remove(it.first.getHash()) }
9464

95-
val allRequests: List<Pair<IKVEntryReference<*>, CompletableObservable<*>>> = regularRequests + prefetchRequests
65+
val allRequests: List<Pair<IKVEntryReference<*>, CompletableObservable<*>>> = regularRequests
9666

9767
val entries: Map<String, IKVValue?> = executeBulkQuery(
9868
regularRequests.asSequence().map { obj -> obj.first }.toSet().toList(),
99-
prefetchRequests.asSequence().map { obj -> obj.first }.toSet().toList(),
10069
)
10170
for (request in allRequests) {
10271
(request.second as CompletableObservable<IKVValue?>).complete(entries[request.first.getHash()])
@@ -125,95 +94,3 @@ private fun <T> Sequence<T>.tailSequence(size: Int, tailSize: Int): Sequence<T>
12594
private fun <T> Collection<T>.tailSequence(tailSize: Int): Sequence<T> {
12695
return asSequence().tailSequence(size, tailSize)
12796
}
128-
129-
@Deprecated("Prefetching will be replaced by usages of IAsyncNode")
130-
interface IPrefetchGoal {
131-
fun loadRequest(bulkQuery: IBulkQuery): IStream.Many<Any?>
132-
}
133-
134-
@Deprecated("Prefetching will be replaced by usages of IAsyncNode")
135-
private class PrefetchQueue(val bulkQuery: IBulkQuery, val queueSizeLimit: Int) {
136-
private val goals: MutableMap<IPrefetchGoal, QueuedGoal> = LinkedHashMap()
137-
private var previousRequests: MutableMap<String, PrefetchRequest<*>> = LinkedHashMap()
138-
private var nextRequests: MutableMap<String, PrefetchRequest<*>> = LinkedHashMap()
139-
private var currentGoal: QueuedGoal? = null
140-
private var anyEntryRequested = false
141-
var requestFilter: (IKVEntryReference<*>) -> Boolean = { true }
142-
143-
fun isLoadingGoal() = currentGoal != null
144-
145-
fun fillRequestsQueue(requestLimit: Int) {
146-
if (requestLimit <= 0) return
147-
148-
previousRequests = nextRequests
149-
nextRequests = LinkedHashMap()
150-
151-
for (goal in goals.values.toList().sortedByDescending { it.prefetchLevel }.asReversed()) {
152-
if (nextRequests.size >= requestLimit) break
153-
executeRequests(goal)
154-
}
155-
}
156-
157-
fun getRequests(limit: Int): List<Pair<IKVEntryReference<*>, CompletableObservable<*>>> {
158-
return nextRequests.entries.tailSequence(limit)
159-
.map { it.value.hash to it.value.result }
160-
.toList()
161-
}
162-
163-
fun addGoal(goal: IPrefetchGoal) {
164-
val newLevel = currentGoal?.prefetchLevel?.let { it + 1 } ?: 0
165-
// remove and re-add to move it to the end of the queue
166-
val queuedGoal = goals.remove(goal)?.also { it.prefetchLevel = minOf(it.prefetchLevel, newLevel) } ?: QueuedGoal(goal, newLevel)
167-
goals[goal] = queuedGoal
168-
trimQueue()
169-
}
170-
171-
fun <T : IKVValue> addRequest(hash: IKVEntryReference<T>, result: CompletableObservable<T?>) {
172-
addRequest(hash, checkNotNull(currentGoal) { "Not loading any goal" }, result)
173-
}
174-
175-
private fun <T : IKVValue> addRequest(hash: IKVEntryReference<T>, goal: QueuedGoal, result: CompletableObservable<T?>) {
176-
anyEntryRequested = true
177-
178-
val request = (previousRequests[hash.getHash()] ?: nextRequests[hash.getHash()])?.also {
179-
require(result == it.result)
180-
it.prefetchLevel = minOf(it.prefetchLevel, goal.prefetchLevel)
181-
} ?: PrefetchRequest(hash, result, goal.prefetchLevel)
182-
183-
if (!request.result.isDone() && requestFilter(request.hash)) {
184-
nextRequests[hash.getHash()] = request
185-
}
186-
trimQueue()
187-
}
188-
189-
fun <T : IKVValue> getValueInstance(hash: IKVEntryReference<T>): CompletableObservable<T?>? {
190-
return ((nextRequests[hash.getHash()] ?: previousRequests[hash.getHash()]) as PrefetchRequest<T>?)?.result
191-
}
192-
193-
private fun trimQueue() {
194-
if (goals.size > queueSizeLimit * 2) {
195-
val toRemove = goals.entries.sortedBy { it.value.prefetchLevel }.drop(goals.size - queueSizeLimit).map { it.key }
196-
toRemove.forEach { goals.remove(it) }
197-
}
198-
}
199-
200-
private fun executeRequests(goal: QueuedGoal) {
201-
val previousGoal = currentGoal
202-
val previousAnyEntryRequested = anyEntryRequested
203-
try {
204-
currentGoal = goal
205-
anyEntryRequested = false
206-
goal.goal.loadRequest(bulkQuery).iterateSynchronous { }
207-
if (!anyEntryRequested) {
208-
goals.remove(goal.goal)
209-
}
210-
} finally {
211-
anyEntryRequested = previousAnyEntryRequested
212-
currentGoal = previousGoal
213-
}
214-
}
215-
216-
private inner class QueuedGoal(val goal: IPrefetchGoal, var prefetchLevel: Int)
217-
218-
private inner class PrefetchRequest<E : IKVValue>(val hash: IKVEntryReference<E>, val result: CompletableObservable<E?>, var prefetchLevel: Int)
219-
}

model-datastructure/src/commonMain/kotlin/org/modelix/model/lazy/IBulkQuery.kt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@ import org.modelix.streams.IStream
66

77
@Deprecated("use IAsyncStore")
88
interface IBulkQuery {
9-
@Deprecated("Prefetching will be replaced by usages of IAsyncNode")
10-
fun offerPrefetch(key: IPrefetchGoal)
119
fun executeQuery()
1210
fun <I, O> flatMap(input: Iterable<I>, f: (I) -> IStream.Many<O>): IStream.Many<O> = IStream.many(input).flatMap { f(it) }
1311
fun <T> constant(value: T): IStream.One<T> = IStream.of(value)

model-datastructure/src/commonMain/kotlin/org/modelix/model/lazy/IDeserializingKeyValueStore.kt

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,7 @@ interface IDeserializingKeyValueStore {
1212
operator fun <T> get(hash: String, deserializer: (String) -> T): T?
1313
fun <T> getIfCached(hash: String, deserializer: (String) -> T, isPrefetch: Boolean): T?
1414
fun <T> getAll(hash: Iterable<String>, deserializer: (String, String) -> T): Iterable<T>
15-
fun <T : IKVValue> getAll(regular: List<IKVEntryReference<T>>, prefetch: List<IKVEntryReference<T>>): Map<String, T?> = throw UnsupportedOperationException()
15+
fun <T : IKVValue> getAll(regular: List<IKVEntryReference<T>>): Map<String, T?> = throw UnsupportedOperationException()
1616
fun put(hash: String, deserialized: Any, serialized: String)
17-
18-
@Deprecated("BulkQuery is now responsible for prefetching")
19-
fun prefetch(hash: String)
20-
2117
fun getAsyncStore(): IAsyncObjectStore = LegacyDeserializingStoreAsAsyncStore(this) // LegacyDeserializingStoreAsAsyncStore(this) // BulkAsyncStore(CachingAsyncStore(LegacyKeyValueStoreAsAsyncStore(keyValueStore)))
2218
}

model-datastructure/src/commonMain/kotlin/org/modelix/model/lazy/IndirectObjectStore.kt

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,11 @@ abstract class IndirectObjectStore : IDeserializingKeyValueStore {
2222
return getStore().getAll(hash, deserializer)
2323
}
2424

25-
override fun <T : IKVValue> getAll(regular: List<IKVEntryReference<T>>, prefetch: List<IKVEntryReference<T>>): Map<String, T?> {
26-
return getStore().getAll(regular, prefetch)
25+
override fun <T : IKVValue> getAll(regular: List<IKVEntryReference<T>>): Map<String, T?> {
26+
return getStore().getAll(regular)
2727
}
2828

2929
override fun put(hash: String, deserialized: Any, serialized: String) {
3030
getStore().put(hash, deserialized, serialized)
3131
}
32-
33-
override fun prefetch(hash: String) {
34-
getStore().prefetch(hash)
35-
}
3632
}

model-datastructure/src/commonMain/kotlin/org/modelix/model/lazy/NonBulkQuery.kt

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,6 @@ import org.modelix.streams.IStream
66
@Deprecated("use IAsyncStore")
77
class NonBulkQuery(private val store: IDeserializingKeyValueStore) : IBulkQuery {
88

9-
override fun offerPrefetch(key: IPrefetchGoal) {
10-
// Since no real bulk queries are executed, prefetching doesn't provide any benefit.
11-
}
12-
139
override fun <T : IKVValue> query(hash: IKVEntryReference<T>): IStream.ZeroOrOne<T> {
1410
return IStream.of(hash.getValue(store))
1511
}

0 commit comments

Comments
 (0)