Skip to content

Commit 4536ef0

Browse files
committed
perf(model-server): optimized memory consumption of computeDelta
The VersionDelta computation was cached in case there are multiple clients listening for changes on the same branch. For large updates this can consume a lot of memory. Using SoftReferences allows the garbage collector to clear cache entries. There was also an unnecessary caching of deltas without a second version, which resulted in caching the whole model.
1 parent 144fab7 commit 4536ef0

File tree

1 file changed

+34
-10
lines changed

1 file changed

+34
-10
lines changed

model-server/src/main/kotlin/org/modelix/model/server/handlers/RepositoriesManager.kt

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@
1313
*/
1414
package org.modelix.model.server.handlers
1515

16+
import kotlinx.coroutines.flow.Flow
17+
import kotlinx.coroutines.flow.asFlow
18+
import kotlinx.coroutines.flow.emptyFlow
19+
import kotlinx.coroutines.flow.flow
20+
import kotlinx.coroutines.flow.map
1621
import kotlinx.datetime.Clock
1722
import org.apache.commons.collections4.map.LRUMap
1823
import org.modelix.model.VersionMerger
@@ -25,12 +30,14 @@ import org.modelix.model.api.runSynchronized
2530
import org.modelix.model.lazy.BranchReference
2631
import org.modelix.model.lazy.CLTree
2732
import org.modelix.model.lazy.CLVersion
33+
import org.modelix.model.lazy.KVEntryReference
2834
import org.modelix.model.lazy.RepositoryId
2935
import org.modelix.model.lazy.computeDelta
3036
import org.modelix.model.metameta.MetaModelBranch
3137
import org.modelix.model.server.store.IStoreClient
3238
import org.modelix.model.server.store.LocalModelClient
3339
import org.modelix.model.server.store.pollEntry
40+
import java.lang.ref.SoftReference
3441
import java.util.UUID
3542

3643
class RepositoriesManager(val client: LocalModelClient) {
@@ -200,18 +207,35 @@ class RepositoriesManager(val client: LocalModelClient) {
200207
?: throw IllegalStateException("No version found for branch '${branch.branchName}' in repository '${branch.repositoryId}'")
201208
}
202209

203-
private val deltaCache = LRUMap<Pair<String, String?>, Lazy<Map<String, String>>>(10)
204-
fun computeDelta(versionHash: String, baseVersionHash: String?): Map<String, String> {
205-
return runSynchronized(deltaCache) {
206-
deltaCache.getOrPut(versionHash to baseVersionHash) {
207-
// lazy { ... } allows to run the computation without locking deltaCache
208-
lazy {
209-
val version = CLVersion(versionHash, client.storeCache)
210-
val baseVersion = baseVersionHash?.let { CLVersion(it, client.storeCache) }
211-
version.computeDelta(baseVersion)
210+
private val deltaCache = LRUMap<Pair<String, String?>, SoftReference<Lazy<Map<String, String>>>>(10)
211+
fun computeDelta(versionHash: String, baseVersionHash: String?): Flow<Pair<String, String>> {
212+
if (versionHash == baseVersionHash) return emptyFlow()
213+
if (baseVersionHash == null) {
214+
// no need to cache anything if there is no delta computation happening
215+
return flow {
216+
suspend fun emitObjects(entry: KVEntryReference<*>) {
217+
emit(entry.getHash() to client.get(entry.getHash())!!)
218+
for (referencedEntry in entry.getValue(client.storeCache).getReferencedEntries()) {
219+
emitObjects(referencedEntry)
220+
}
212221
}
222+
223+
emit(versionHash to client.get(versionHash)!!)
224+
val version = CLVersion(versionHash, client.storeCache)
225+
emitObjects(version.treeHash!!)
213226
}
214-
}.value
227+
}
228+
229+
return runSynchronized(deltaCache) {
230+
val key = versionHash to baseVersionHash
231+
deltaCache.get(key)?.get() ?: lazy {
232+
// lazy { ... } allows to run the computation without locking deltaCache
233+
// SoftReference because deltas can be very large
234+
val version = CLVersion(versionHash, client.storeCache)
235+
val baseVersion = CLVersion(baseVersionHash, client.storeCache)
236+
version.computeDelta(baseVersion)
237+
}.also { deltaCache[key] = SoftReference(it) }
238+
}.value.entries.asFlow().map { it.toPair() }
215239
}
216240

217241
private fun branchKey(branch: BranchReference): String {

0 commit comments

Comments
 (0)