Skip to content

Commit 79af748

Browse files
author
Oleksandr Dzhychko
committed
perf(model-server): avoid starting suspendable transaction inside suspendable transactions
This avoids blocking one additional thread.
1 parent 8308e42 commit 79af748

File tree

2 files changed

+56
-13
lines changed

2 files changed

+56
-13
lines changed

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

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,11 @@ class KeyValueLikeModelServer(val repositoriesManager: RepositoriesManager) {
112112
if (isHealthy()) {
113113
call.respondText(text = "healthy", contentType = ContentType.Text.Plain, status = HttpStatusCode.OK)
114114
} else {
115-
call.respondText(text = "not healthy", contentType = ContentType.Text.Plain, status = HttpStatusCode.InternalServerError)
115+
call.respondText(
116+
text = "not healthy",
117+
contentType = ContentType.Text.Plain,
118+
status = HttpStatusCode.InternalServerError,
119+
)
116120
}
117121
}
118122
get<Paths.getHeaders> {
@@ -317,18 +321,23 @@ class KeyValueLikeModelServer(val repositoriesManager: RepositoriesManager) {
317321
HashUtil.isSha256(key) -> {
318322
hashedObjects[key] = value ?: throw IllegalArgumentException("No value provided for $key")
319323
}
324+
320325
BranchReference.tryParseBranch(key) != null -> {
321326
branchChanges[BranchReference.tryParseBranch(key)!!] = value
322327
}
328+
323329
key.startsWith(PROTECTED_PREFIX) -> {
324330
throw NoPermissionException("Access to keys starting with '$PROTECTED_PREFIX' is only permitted to the model server itself.")
325331
}
332+
326333
key.startsWith(RepositoriesManager.KEY_PREFIX) -> {
327334
throw NoPermissionException("Access to keys starting with '${RepositoriesManager.KEY_PREFIX}' is only permitted to the model server itself.")
328335
}
336+
329337
key == RepositoriesManager.LEGACY_SERVER_ID_KEY || key == RepositoriesManager.LEGACY_SERVER_ID_KEY2 -> {
330338
throw NoPermissionException("'$key' is read-only.")
331339
}
340+
332341
else -> {
333342
userDefinedEntries[key] = value
334343
}
@@ -342,9 +351,9 @@ class KeyValueLikeModelServer(val repositoriesManager: RepositoriesManager) {
342351
storeClient.putAll(userDefinedEntries)
343352
for ((branch, value) in branchChanges) {
344353
if (value == null) {
345-
runBlocking { repositoriesManager.removeBranches(branch.repositoryId, setOf(branch.branchName)) }
354+
repositoriesManager.removeBranchesBlocking(branch.repositoryId, setOf(branch.branchName))
346355
} else {
347-
runBlocking { repositoriesManager.mergeChanges(branch, value) }
356+
repositoriesManager.mergeChangesBlocking(branch, value)
348357
}
349358
}
350359
}
@@ -366,7 +375,10 @@ class KeyValueLikeModelServer(val repositoriesManager: RepositoriesManager) {
366375
if (key.startsWith(RepositoriesManager.KEY_PREFIX)) {
367376
throw NoPermissionException("Access to keys starting with '${RepositoriesManager.KEY_PREFIX}' is only permitted to the model server itself.")
368377
}
369-
if ((key == RepositoriesManager.LEGACY_SERVER_ID_KEY || key == RepositoriesManager.LEGACY_SERVER_ID_KEY2) && type.includes(EPermissionType.WRITE)) {
378+
if ((key == RepositoriesManager.LEGACY_SERVER_ID_KEY || key == RepositoriesManager.LEGACY_SERVER_ID_KEY2) && type.includes(
379+
EPermissionType.WRITE,
380+
)
381+
) {
370382
throw NoPermissionException("'$key' is read-only.")
371383
}
372384
if (HashUtil.isSha256(key)) {

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

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -200,8 +200,18 @@ class RepositoriesManager(val client: LocalModelClient) {
200200
}
201201

202202
suspend fun removeBranches(repository: RepositoryId, branchNames: Set<String>) {
203+
return store.runTransactionSuspendable {
204+
removeBranchesBlocking(repository, branchNames)
205+
}
206+
}
207+
208+
/**
209+
* Same as [removeBranches] but blocking.
210+
* Caller is expected to execute it outside the request thread.
211+
*/
212+
fun removeBranchesBlocking(repository: RepositoryId, branchNames: Set<String>) {
203213
if (branchNames.isEmpty()) return
204-
store.runTransactionSuspendable {
214+
store.runTransaction {
205215
val key = branchListKey(repository)
206216
val existingBranches = store[key]?.lines()?.toSet() ?: emptySet()
207217
val remainingBranches = existingBranches - branchNames
@@ -213,9 +223,18 @@ class RepositoriesManager(val client: LocalModelClient) {
213223
}
214224

215225
suspend fun mergeChanges(branch: BranchReference, newVersionHash: String): String {
216-
var result: String? = null
217-
store.runTransactionSuspendable {
218-
val headHash = getVersionHashInsideTransaction(branch)
226+
return store.runTransactionSuspendable {
227+
mergeChangesBlocking(branch, newVersionHash)
228+
}
229+
}
230+
231+
/**
232+
* Same as [mergeChanges] but blocking.
233+
* Caller is expected to execute it outside the request thread.
234+
*/
235+
fun mergeChangesBlocking(branch: BranchReference, newVersionHash: String): String {
236+
return store.runTransaction {
237+
val headHash = getVersionHashBlocking(branch)
219238
val mergedHash = if (headHash == null) {
220239
newVersionHash
221240
} else {
@@ -232,9 +251,8 @@ class RepositoriesManager(val client: LocalModelClient) {
232251
putVersionHash(branch, mergedHash)
233252
ensureRepositoriesAreInList(setOf(branch.repositoryId))
234253
ensureBranchesAreInList(branch.repositoryId, setOf(branch.branchName))
235-
result = mergedHash
254+
mergedHash
236255
}
237-
return result!!
238256
}
239257

240258
suspend fun getVersion(branch: BranchReference): CLVersion? {
@@ -243,12 +261,24 @@ class RepositoriesManager(val client: LocalModelClient) {
243261

244262
suspend fun getVersionHash(branch: BranchReference): String? {
245263
return store.runTransactionSuspendable {
246-
getVersionHashInsideTransaction(branch)
264+
getVersionHashBlocking(branch)
247265
}
248266
}
249267

250-
private fun getVersionHashInsideTransaction(branch: BranchReference): String? {
251-
return store[branchKey(branch)] ?: store[legacyBranchKey(branch)]?.also { store.put(branchKey(branch), it, true) }
268+
/**
269+
* Same as [getVersionHash] but blocking.
270+
* Caller is expected to execute it outside the request thread.
271+
*/
272+
private fun getVersionHashBlocking(branch: BranchReference): String? {
273+
return store.runTransaction {
274+
store[branchKey(branch)] ?: store[legacyBranchKey(branch)]?.also {
275+
store.put(
276+
branchKey(branch),
277+
it,
278+
true,
279+
)
280+
}
281+
}
252282
}
253283

254284
private fun putVersionHash(branch: BranchReference, hash: String?) {
@@ -368,6 +398,7 @@ class ObjectDataMap(private val byHashObjects: Map<String, String>) : ObjectData
368398
init {
369399
HashUtil.checkObjectHashes(byHashObjects)
370400
}
401+
371402
override suspend fun asMap(): Map<String, String> = byHashObjects
372403
override fun asFlow(): Flow<Pair<String, String>> = byHashObjects.entries.asFlow().map { it.toPair() }
373404
}

0 commit comments

Comments
 (0)