Skip to content

Commit 2df3031

Browse files
authored
Merge pull request #69 from modelix/fix-model-client-v2
Some fixes after testing the new model client in modelix.text-editor
2 parents 5562b68 + b408d87 commit 2df3031

File tree

4 files changed

+115
-13
lines changed

4 files changed

+115
-13
lines changed

model-api/src/commonMain/kotlin/org/modelix/model/api/IBranch.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,9 @@ interface IBranch {
4141
fun addListener(l: IBranchListener)
4242
fun removeListener(l: IBranchListener)
4343
}
44+
45+
interface IBranchWrapper : IBranch {
46+
fun unwrapBranch(): IBranch
47+
}
48+
49+
fun IBranch.deepUnwrap(): IBranch = if (this is IBranchWrapper) unwrapBranch().deepUnwrap() else this
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/*
2+
* Licensed under the Apache License, Version 2.0 (the "License");
3+
* you may not use this file except in compliance with the License.
4+
* You may obtain a copy of the License at
5+
*
6+
* http://www.apache.org/licenses/LICENSE-2.0
7+
*
8+
* Unless required by applicable law or agreed to in writing, software
9+
* distributed under the License is distributed on an "AS IS" BASIS,
10+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the License for the specific language governing permissions and
12+
* limitations under the License.
13+
*/
14+
package org.modelix.model
15+
16+
import org.modelix.model.api.IBranch
17+
import org.modelix.model.api.IBranchWrapper
18+
import org.modelix.model.api.IConcept
19+
import org.modelix.model.api.IConceptReference
20+
import org.modelix.model.api.INodeReference
21+
import org.modelix.model.api.IReadTransaction
22+
import org.modelix.model.api.ITransaction
23+
import org.modelix.model.api.ITree
24+
import org.modelix.model.api.IWriteTransaction
25+
26+
class AutoTransactionsBranch(private val branch: IBranch) : IBranch by branch, IBranchWrapper {
27+
override val transaction: ITransaction
28+
get() = if (isInTransaction()) branch.transaction else AutoReadTransaction(branch)
29+
override val readTransaction: IReadTransaction
30+
get() = if (isInTransaction()) branch.readTransaction else AutoReadTransaction(branch)
31+
override val writeTransaction: IWriteTransaction
32+
get() = if (isInTransaction()) branch.writeTransaction else AutoWriteTransaction(branch)
33+
34+
private fun isInTransaction() = branch.canRead()
35+
override fun unwrapBranch(): IBranch = branch
36+
}
37+
38+
open class AutoTransaction(override val branch: IBranch) : ITransaction {
39+
override val tree: ITree
40+
get() = branch.computeReadT { it.tree }
41+
42+
override fun containsNode(nodeId: Long): Boolean = branch.computeReadT { it.containsNode(nodeId) }
43+
override fun getConcept(nodeId: Long): IConcept? = branch.computeReadT { it.getConcept(nodeId) }
44+
override fun getConceptReference(nodeId: Long): IConceptReference? = branch.computeReadT { it.getConceptReference(nodeId) }
45+
override fun getParent(nodeId: Long): Long = branch.computeReadT { it.getParent(nodeId) }
46+
override fun getRole(nodeId: Long): String? = branch.computeReadT { it.getRole(nodeId) }
47+
override fun getProperty(nodeId: Long, role: String): String? = branch.computeReadT { it.getProperty(nodeId, role) }
48+
override fun getReferenceTarget(sourceId: Long, role: String): INodeReference? = branch.computeReadT { it.getReferenceTarget(sourceId, role) }
49+
override fun getChildren(parentId: Long, role: String?): Iterable<Long> = branch.computeReadT { it.getChildren(parentId, role) }
50+
override fun getAllChildren(parentId: Long): Iterable<Long> = branch.computeReadT { it.getAllChildren(parentId) }
51+
override fun getReferenceRoles(sourceId: Long): Iterable<String> = branch.computeReadT { it.getReferenceRoles(sourceId) }
52+
override fun getPropertyRoles(sourceId: Long): Iterable<String> = branch.computeReadT { it.getPropertyRoles(sourceId) }
53+
override fun getUserObject(key: Any): Any? = null
54+
override fun putUserObject(key: Any, value: Any?) {}
55+
}
56+
57+
class AutoReadTransaction(branch: IBranch) : AutoTransaction(branch), IReadTransaction
58+
59+
class AutoWriteTransaction(branch: IBranch) : AutoTransaction(branch), IWriteTransaction {
60+
override fun setProperty(nodeId: Long, role: String, value: String?) =
61+
branch.computeWriteT { it.setProperty(nodeId, role, value) }
62+
override fun setReferenceTarget(sourceId: Long, role: String, target: INodeReference?) =
63+
branch.computeWriteT { it.setReferenceTarget(sourceId, role, target) }
64+
override fun moveChild(newParentId: Long, newRole: String?, newIndex: Int, childId: Long) =
65+
branch.computeWriteT { it.moveChild(newParentId, newRole, newIndex, childId) }
66+
override fun addNewChild(parentId: Long, role: String?, index: Int, concept: IConcept?): Long =
67+
branch.computeWriteT { it.addNewChild(parentId, role, index, concept) }
68+
override fun addNewChild(parentId: Long, role: String?, index: Int, concept: IConceptReference?): Long =
69+
branch.computeWriteT { it.addNewChild(parentId, role, index, concept) }
70+
override fun addNewChild(parentId: Long, role: String?, index: Int, childId: Long, concept: IConcept?) =
71+
branch.computeWriteT { it.addNewChild(parentId, role, index, childId, concept) }
72+
override fun addNewChild(
73+
parentId: Long,
74+
role: String?,
75+
index: Int,
76+
childId: Long,
77+
concept: IConceptReference?
78+
) = branch.computeWriteT { it.addNewChild(parentId, role, index, childId, concept) }
79+
override fun deleteNode(nodeId: Long) = branch.computeWriteT { it.deleteNode(nodeId) }
80+
81+
override var tree: ITree
82+
get() = branch.computeReadT { it.tree }
83+
set(value) { branch.computeWriteT { it.tree = value } }
84+
}
85+
86+
fun IBranch.withAutoTransactions() = AutoTransactionsBranch(this)

model-client/src/commonMain/kotlin/org/modelix/model/client2/ReplicatedModel.kt

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,13 @@ class ReplicatedModel(val client: IModelClientV2, val branchRef: BranchReference
9191
if (newRemoteVersion.getContentHash() != localVersion.getContentHash()) {
9292
otBranch.runWrite {
9393
applyPendingLocalChanges()
94-
localVersion = VersionMerger(newRemoteVersion.store, client.getIdGenerator())
95-
.mergeChange(localVersion, newRemoteVersion)
94+
try {
95+
localVersion = VersionMerger(newRemoteVersion.store, client.getIdGenerator())
96+
.mergeChange(localVersion, newRemoteVersion)
97+
} catch (ex: Exception) {
98+
LOG.warn(ex) { "Failed to merge remote version $newRemoteVersion into local version $localVersion. Resetting to remote version." }
99+
localVersion = newRemoteVersion
100+
}
96101
rawBranch.writeTransaction.tree = localVersion.tree
97102
}
98103
}
@@ -136,6 +141,10 @@ class ReplicatedModel(val client: IModelClientV2, val branchRef: BranchReference
136141
Started,
137142
Disposed
138143
}
144+
145+
companion object {
146+
private val LOG = mu.KotlinLogging.logger { }
147+
}
139148
}
140149

141150
fun IModelClientV2.getReplicatedModel(branchRef: BranchReference, query: ModelQuery? = null): ReplicatedModel {

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

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,25 +16,23 @@ package org.modelix.model.server.handlers
1616

1717
import io.ktor.http.*
1818
import io.ktor.server.application.*
19-
import io.ktor.server.auth.*
2019
import io.ktor.server.plugins.*
21-
import io.ktor.server.plugins.cors.routing.*
22-
import io.ktor.server.plugins.statuspages.*
2320
import io.ktor.server.request.*
2421
import io.ktor.server.response.*
2522
import io.ktor.server.routing.*
2623
import io.ktor.server.websocket.*
2724
import io.ktor.util.pipeline.*
2825
import io.ktor.websocket.*
26+
import kotlinx.coroutines.Dispatchers
2927
import kotlinx.coroutines.Job
28+
import kotlinx.coroutines.withContext
3029
import kotlinx.serialization.ExperimentalSerializationApi
3130
import kotlinx.serialization.encodeToString
3231
import kotlinx.serialization.json.DecodeSequenceMode
3332
import kotlinx.serialization.json.Json
34-
import kotlinx.serialization.json.decodeFromStream
3533
import kotlinx.serialization.json.decodeToSequence
3634
import kotlinx.serialization.json.encodeToStream
37-
import org.modelix.authorization.*
35+
import org.modelix.authorization.getUserName
3836
import org.modelix.model.lazy.RepositoryId
3937
import org.modelix.model.persistent.HashUtil
4038
import org.modelix.model.server.api.ModelQuery
@@ -121,7 +119,7 @@ class ModelReplicationServer(val repositoriesManager: RepositoriesManager) {
121119
call.respondDelta(versionHash, baseVersionHash)
122120
}
123121
post {
124-
val deltaFromClient = Json.decodeFromStream<VersionDelta>(call.receiveStream())
122+
val deltaFromClient = call.receive<VersionDelta>()
125123
storeClient.putAll(deltaFromClient.objects.associateBy { HashUtil.sha256(it) })
126124
val mergedHash = repositoriesManager.mergeChanges(branchRef(), deltaFromClient.versionHash)
127125
call.respondDelta(mergedHash, deltaFromClient.versionHash)
@@ -168,11 +166,14 @@ class ModelReplicationServer(val repositoriesManager: RepositoriesManager) {
168166
}
169167
route("objects") {
170168
post {
171-
Json.decodeToSequence<String>(call.receiveStream(), DecodeSequenceMode.ARRAY_WRAPPED)
172-
.chunked(5000)
173-
.forEach { values ->
174-
storeClient.putAll(values.associateBy { HashUtil.sha256(it) }, true)
175-
}
169+
withContext(Dispatchers.IO) {
170+
Json.decodeToSequence<String>(call.receiveStream(), DecodeSequenceMode.ARRAY_WRAPPED)
171+
.chunked(5000)
172+
.forEach { values ->
173+
storeClient.putAll(values.associateBy { HashUtil.sha256(it) }, true)
174+
}
175+
}
176+
call.respondText("OK")
176177
}
177178
get("{hash}") {
178179
val key = call.parameters["hash"]!!

0 commit comments

Comments
 (0)