@@ -15,6 +15,11 @@ package org.modelix.model.server.handlers
15
15
16
16
import kotlinx.datetime.Clock
17
17
import org.modelix.model.VersionMerger
18
+ import org.modelix.model.api.IBranch
19
+ import org.modelix.model.api.IReadTransaction
20
+ import org.modelix.model.api.ITree
21
+ import org.modelix.model.api.IdGeneratorDummy
22
+ import org.modelix.model.api.PBranch
18
23
import org.modelix.model.lazy.BranchReference
19
24
import org.modelix.model.lazy.BulkQuery
20
25
import org.modelix.model.lazy.CLHamtNode
@@ -24,32 +29,34 @@ import org.modelix.model.lazy.IDeserializingKeyValueStore
24
29
import org.modelix.model.lazy.KVEntryReference
25
30
import org.modelix.model.lazy.ObjectStoreCache
26
31
import org.modelix.model.lazy.RepositoryId
32
+ import org.modelix.model.metameta.MetaModelBranch
27
33
import org.modelix.model.persistent.CPNode
28
34
import org.modelix.model.server.store.IStoreClient
29
35
import org.modelix.model.server.store.LocalModelClient
30
36
import org.modelix.model.server.store.pollEntry
31
37
32
- /* *
33
- * Multiple instances can be used at the same time, because there is no state outside the store.
34
- */
35
38
class RepositoriesManager (val client : LocalModelClient ) {
39
+ init {
40
+ migrateLegacyRepositoriesList()
41
+ }
42
+
36
43
private val store: IStoreClient get() = client.store
37
44
38
45
fun generateClientId (repositoryId : RepositoryId ): Long {
39
46
return client.store.generateId(" $KEY_PREFIX :${repositoryId.id} :clientId" )
40
47
}
41
48
42
- fun getRepositoryNames (): Set <String > {
43
- return store[REPOSITORIES_LIST_KEY ]?.lines()?.toSet() ? : emptySet()
49
+ fun getRepositories (): Set <RepositoryId > {
50
+ return store[REPOSITORIES_LIST_KEY ]?.lines()?.map { RepositoryId (it) }?. toSet() ? : emptySet()
44
51
}
45
52
46
53
fun createRepository (repositoryId : RepositoryId , userName : String? ): CLVersion {
47
54
var initialVersion: CLVersion ? = null
48
55
store.runTransaction {
49
56
val masterBranch = repositoryId.getBranchReference()
50
- val existingRepositories = getRepositoryNames ()
51
- if (existingRepositories.contains(repositoryId.id )) throw RepositoryAlreadyExistsException (repositoryId.id)
52
- store.put(REPOSITORIES_LIST_KEY , (existingRepositories + repositoryId.id ).joinToString(" \n " ), false )
57
+ val existingRepositories = getRepositories ()
58
+ if (existingRepositories.contains(repositoryId)) throw RepositoryAlreadyExistsException (repositoryId.id)
59
+ store.put(REPOSITORIES_LIST_KEY , (existingRepositories + repositoryId).joinToString(" \n " ) { it.id } , false )
53
60
store.put(branchListKey(repositoryId), masterBranch.branchName, false )
54
61
initialVersion = CLVersion .createRegularVersion(
55
62
id = client.idGenerator.generate(),
@@ -71,19 +78,34 @@ class RepositoriesManager(val client: LocalModelClient) {
71
78
/* *
72
79
* Must be executed inside a transaction
73
80
*/
74
- private fun ensureBranchIsInList (branch : BranchReference ) {
75
- val key = branchListKey(branch.repositoryId)
81
+ private fun ensureRepositoriesAreInList (repositoryIds : Set <RepositoryId >) {
82
+ if (repositoryIds.isEmpty()) return
83
+ val key = REPOSITORIES_LIST_KEY
84
+ val existingRepositories = getRepositories()
85
+ val missingRepositories = repositoryIds - existingRepositories
86
+ if (missingRepositories.isNotEmpty()) {
87
+ store.put(key, (existingRepositories + missingRepositories).joinToString(" \n " ) { it.id })
88
+ }
89
+ }
90
+
91
+ /* *
92
+ * Must be executed inside a transaction
93
+ */
94
+ private fun ensureBranchesAreInList (repository : RepositoryId , branchNames : Set <String >) {
95
+ if (branchNames.isEmpty()) return
96
+ val key = branchListKey(repository)
76
97
val existingBranches = store[key]?.lines()?.toSet() ? : emptySet()
77
- if (! existingBranches.contains(branch.branchName)) {
78
- store.put(key, (existingBranches + branch.branchName).joinToString(" \n " ))
98
+ val missingBranches = branchNames - existingBranches
99
+ if (missingBranches.isNotEmpty()) {
100
+ store.put(key, (existingBranches + missingBranches).joinToString(" \n " ))
79
101
}
80
102
}
81
103
82
104
fun mergeChanges (branch : BranchReference , newVersionHash : String ): String {
83
105
var result: String? = null
84
106
store.runTransaction {
85
107
val branchKey = branchKey(branch)
86
- val headHash = store[branchKey] ? : store[legacyBranchKey (branch)]
108
+ val headHash = getVersionHash (branch)
87
109
val mergedHash = if (headHash == null ) {
88
110
newVersionHash
89
111
} else {
@@ -97,15 +119,21 @@ class RepositoriesManager(val client: LocalModelClient) {
97
119
.mergeChange(headVersion, newVersion)
98
120
mergedVersion.hash
99
121
}
100
- store.put(branchKey , mergedHash, false )
101
- ensureBranchIsInList (branch)
122
+ putVersionHash(branch , mergedHash)
123
+ ensureBranchesAreInList (branch.repositoryId, setOf (branch.branchName) )
102
124
result = mergedHash
103
125
}
104
126
return result!!
105
127
}
106
128
107
129
fun getVersionHash (branch : BranchReference ): String? {
108
130
return store[branchKey(branch)]
131
+ ? : store[legacyBranchKey(branch)]?.also { store.put(branchKey(branch), it, true ) }
132
+ }
133
+
134
+ private fun putVersionHash (branch : BranchReference , hash : String ) {
135
+ store.put(branchKey(branch), hash, false )
136
+ store.put(legacyBranchKey(branch), hash, false )
109
137
}
110
138
111
139
suspend fun pollVersionHash (branch : BranchReference , lastKnown : String? ): String {
@@ -181,7 +209,39 @@ class RepositoriesManager(val client: LocalModelClient) {
181
209
}
182
210
183
211
private fun branchListKey (repositoryId : RepositoryId ) = " $KEY_PREFIX :repositories:${repositoryId.id} :branches"
184
-
212
+
213
+ fun migrateLegacyRepositoriesList () {
214
+ val legacyRepositories = listLegacyRepositories().groupBy { it.repositoryId }
215
+ if (legacyRepositories.isNotEmpty()) {
216
+ store.runTransaction {
217
+ ensureRepositoriesAreInList(legacyRepositories.keys)
218
+ for ((legacyRepository, legacyBranches) in legacyRepositories) {
219
+ ensureBranchesAreInList(legacyRepository, legacyBranches.map { it.branchName }.toSet())
220
+ }
221
+ }
222
+ }
223
+ }
224
+
225
+ private fun listLegacyRepositories (): Set <BranchReference > {
226
+ val result: MutableSet <BranchReference > = HashSet ()
227
+ val infoVersionHash = client[RepositoryId (" info" ).getBranchReference().getKey()] ? : return emptySet()
228
+ val infoVersion = CLVersion (infoVersionHash, client.storeCache)
229
+ val infoBranch: IBranch = MetaModelBranch (PBranch (infoVersion.getTree(), IdGeneratorDummy ()))
230
+ infoBranch.runReadT { t: IReadTransaction ->
231
+ for (infoNodeId in t.getChildren(ITree .ROOT_ID , " info" )) {
232
+ for (repositoryNodeId in t.getChildren(infoNodeId, " repositories" )) {
233
+ val repositoryId = t.getProperty(repositoryNodeId, " id" )?.let { RepositoryId (it) } ? : continue
234
+ result.add(repositoryId.getBranchReference())
235
+ for (branchNodeId in t.getChildren(repositoryNodeId, " branches" )) {
236
+ val branchName = t.getProperty(branchNodeId, " name" ) ? : continue
237
+ result.add(repositoryId.getBranchReference(branchName))
238
+ }
239
+ }
240
+ }
241
+ }
242
+ return result
243
+ }
244
+
185
245
companion object {
186
246
const val KEY_PREFIX = " :v2"
187
247
private const val REPOSITORIES_LIST_KEY = " $KEY_PREFIX :repositories"
0 commit comments