Skip to content

Commit e453922

Browse files
author
Oleksandr Dzhychko
authored
Merge pull request #908 from modelix/fix-PrefetchCache-getAll
Implement and fix `PrefetchCache.getAll` methods
2 parents d993dc2 + 1ff9a14 commit e453922

File tree

4 files changed

+141
-54
lines changed

4 files changed

+141
-54
lines changed
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*
2+
* Copyright (c) 2024.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.modelix.model.lazy
18+
19+
import org.modelix.model.IKeyListener
20+
import org.modelix.model.IKeyValueStore
21+
22+
/**
23+
* Internal API.
24+
* Only public for tests.
25+
*/
26+
class AccessTrackingStore(val store: IKeyValueStore) : IKeyValueStore {
27+
val accessedEntries: MutableMap<String, String?> = LinkedHashMap()
28+
29+
override fun newBulkQuery(deserializingCache: IDeserializingKeyValueStore, config: BulkQueryConfiguration): IBulkQuery {
30+
return store.newBulkQuery(deserializingCache, config)
31+
}
32+
33+
override fun get(key: String): String? {
34+
val value = store.get(key)
35+
accessedEntries.put(key, value)
36+
return value
37+
}
38+
39+
override fun getIfCached(key: String): String? {
40+
val value = store.getIfCached(key)
41+
if (value != null) {
42+
accessedEntries[key] = value
43+
}
44+
return value
45+
}
46+
47+
override fun put(key: String, value: String?) {
48+
TODO("Not yet implemented")
49+
}
50+
51+
override fun getAll(keys: Iterable<String>): Map<String, String?> {
52+
val entries = store.getAll(keys)
53+
accessedEntries.putAll(entries)
54+
return entries
55+
}
56+
57+
override fun putAll(entries: Map<String, String?>) {
58+
TODO("Not yet implemented")
59+
}
60+
61+
override fun prefetch(key: String) {
62+
TODO("Not yet implemented")
63+
}
64+
65+
override fun listen(key: String, listener: IKeyListener) {
66+
TODO("Not yet implemented")
67+
}
68+
69+
override fun removeListener(key: String, listener: IKeyListener) {
70+
TODO("Not yet implemented")
71+
}
72+
73+
override fun getPendingSize(): Int {
74+
TODO("Not yet implemented")
75+
}
76+
}

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

Lines changed: 0 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import kotlinx.datetime.Clock
1919
import kotlinx.datetime.Instant
2020
import kotlinx.datetime.TimeZone
2121
import kotlinx.datetime.toInstant
22-
import org.modelix.model.IKeyListener
2322
import org.modelix.model.IKeyValueStore
2423
import org.modelix.model.IVersion
2524
import org.modelix.model.LinearHistory
@@ -403,58 +402,6 @@ private fun trackAccessedEntries(store: IKeyValueStore, body: (IDeserializingKey
403402
return accessTrackingStore.accessedEntries
404403
}
405404

406-
private class AccessTrackingStore(val store: IKeyValueStore) : IKeyValueStore {
407-
val accessedEntries: MutableMap<String, String?> = LinkedHashMap()
408-
409-
override fun newBulkQuery(deserializingCache: IDeserializingKeyValueStore, config: BulkQueryConfiguration): IBulkQuery {
410-
return store.newBulkQuery(deserializingCache, config)
411-
}
412-
413-
override fun get(key: String): String? {
414-
val value = store.get(key)
415-
accessedEntries.put(key, value)
416-
return value
417-
}
418-
419-
override fun getIfCached(key: String): String? {
420-
val value = store.getIfCached(key)
421-
if (value != null) {
422-
accessedEntries[key] = value
423-
}
424-
return value
425-
}
426-
427-
override fun put(key: String, value: String?) {
428-
TODO("Not yet implemented")
429-
}
430-
431-
override fun getAll(keys: Iterable<String>): Map<String, String?> {
432-
val entries = store.getAll(keys)
433-
accessedEntries.putAll(entries)
434-
return entries
435-
}
436-
437-
override fun putAll(entries: Map<String, String?>) {
438-
TODO("Not yet implemented")
439-
}
440-
441-
override fun prefetch(key: String) {
442-
TODO("Not yet implemented")
443-
}
444-
445-
override fun listen(key: String, listener: IKeyListener) {
446-
TODO("Not yet implemented")
447-
}
448-
449-
override fun removeListener(key: String, listener: IKeyListener) {
450-
TODO("Not yet implemented")
451-
}
452-
453-
override fun getPendingSize(): Int {
454-
TODO("Not yet implemented")
455-
}
456-
}
457-
458405
fun CLVersion.runWrite(idGenerator: IIdGenerator, author: String?, body: (IWriteTransaction) -> Unit): CLVersion {
459406
val branch = OTBranch(TreePointer(getTree(), idGenerator), idGenerator, store)
460407
branch.computeWriteT(body)

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

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ package org.modelix.model.lazy
1616
import org.modelix.kotlin.utils.ContextValue
1717
import org.modelix.model.IKeyValueStore
1818
import org.modelix.model.api.ITree
19+
import org.modelix.model.persistent.IKVValue
1920

2021
/**
2122
* There is no size limit. Entries are not evicted.
@@ -55,7 +56,17 @@ class PrefetchCache(private val store: IDeserializingKeyValueStore) : IDeseriali
5556
val missingHashes = hashes.filterNot { entries.containsKey(it) }
5657
val missingValues = store.getAll(missingHashes, deserializer).toList()
5758
val missingEntries = missingHashes.mapIndexed { index, hash -> hash to missingValues[index] }.associate { it }
58-
return hashes.map { missingEntries[it] ?: entries[it] as T }
59+
entries.putAll(missingEntries)
60+
return hashes.map { entries[it] as T }
61+
}
62+
63+
override fun <T : IKVValue> getAll(regular: List<IKVEntryReference<T>>, prefetch: List<IKVEntryReference<T>>): Map<String, T?> {
64+
val missingRegular = regular.filterNot { entries.containsKey(it.getHash()) }
65+
val missingPrefetch = prefetch.filterNot { entries.containsKey(it.getHash()) }
66+
val missingEntries = store.getAll(missingRegular, missingPrefetch)
67+
entries.putAll(missingEntries)
68+
val regularAndPrefetch = regular.asSequence() + prefetch.asSequence()
69+
return regularAndPrefetch.associate { it.getHash() to entries[it.getHash()] as T? }
5970
}
6071

6172
override fun put(hash: String, deserialized: Any, serialized: String) {
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import org.modelix.model.lazy.AccessTrackingStore
2+
import org.modelix.model.lazy.NonCachingObjectStore
3+
import org.modelix.model.lazy.PrefetchCache
4+
import org.modelix.model.lazy.WrittenEntry
5+
import org.modelix.model.persistent.CPNode
6+
import org.modelix.model.persistent.MapBasedStore
7+
import kotlin.test.Test
8+
import kotlin.test.assertEquals
9+
import kotlin.test.assertTrue
10+
11+
class PrefetchCacheTest {
12+
13+
private val keyValueStore = MapBasedStore()
14+
private val accessTrackingKeyValueStore = AccessTrackingStore(keyValueStore)
15+
private val deserializingKeyValueStore = NonCachingObjectStore(accessTrackingKeyValueStore)
16+
private val prefetchCache = PrefetchCache(deserializingKeyValueStore)
17+
18+
@Test
19+
fun entriesAreCachedAfterGettingMultipleEntriesAsIterable() {
20+
keyValueStore.put("key", "value")
21+
prefetchCache.getAll(listOf("key")) { _, value -> value }
22+
accessTrackingKeyValueStore.accessedEntries.clear()
23+
24+
val result = prefetchCache.getAll(listOf("key")) { _, value -> value }
25+
26+
assertEquals(result, listOf("value"))
27+
assertTrue(accessTrackingKeyValueStore.accessedEntries.isEmpty())
28+
}
29+
30+
@Test
31+
fun entriesAreCachedAfterGettingMultipleEntriesAsMap() {
32+
val regularKey = "regularKey"
33+
val prefetchKey = "prefetchKey"
34+
val nodeForRegularKey = CPNode.create(
35+
2, null, 1, null, LongArray(0),
36+
emptyArray(), emptyArray(), emptyArray(), emptyArray(),
37+
)
38+
val nodeForPrefetchKey = CPNode.create(
39+
3, null, 1, null, LongArray(0),
40+
emptyArray(), emptyArray(), emptyArray(), emptyArray(),
41+
)
42+
keyValueStore.putAll(mapOf(regularKey to nodeForRegularKey.serialize(), prefetchKey to nodeForPrefetchKey.serialize()))
43+
val regularKeyReference = WrittenEntry(regularKey) { nodeForRegularKey }
44+
val prefetchKeyReference = WrittenEntry(prefetchKey) { nodeForPrefetchKey }
45+
prefetchCache.getAll(listOf(regularKeyReference), listOf(prefetchKeyReference))
46+
accessTrackingKeyValueStore.accessedEntries.clear()
47+
48+
val result = prefetchCache.getAll(listOf(regularKeyReference), listOf(prefetchKeyReference))
49+
50+
assertEquals(result, mapOf(regularKey to nodeForRegularKey, prefetchKey to nodeForPrefetchKey))
51+
assertTrue(accessTrackingKeyValueStore.accessedEntries.isEmpty())
52+
}
53+
}

0 commit comments

Comments
 (0)