Skip to content

Commit

Permalink
Server Synchronizer: ID Sequence
Browse files Browse the repository at this point in the history
Server now calculates an ID sequence for the clients to follow
  • Loading branch information
Wavesonics committed Apr 28, 2023
1 parent 607ba77 commit 282c4e8
Show file tree
Hide file tree
Showing 10 changed files with 71 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ data class ProjectSynchronizationBegan(
val syncId: String,
val lastSync: Instant,
val lastId: Int,
val idSequence: List<Int>,
val deletedIds: Set<Int>
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,17 @@ class ClientProjectSynchronizer(
onConflict: EntityConflictHandler<ApiProjectEntity>
): Boolean {
var allSuccess = true
for (thisId in 1..maxId) {

val maxServerId = serverSyncData.idSequence.max()
// Add local IDs on top of server sequence
val combinedSequence = if(maxId > maxServerId) {
val localIds = (maxServerId+1 .. maxId).toList()
serverSyncData.idSequence + localIds
} else {
serverSyncData.idSequence
}

for (thisId in combinedSequence) {
if (thisId in combinedDeletions) {
Napier.d("Skipping deleted ID $thisId")
continue
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,12 @@ class ProjectRepository(
)
}

val updateSequence = getUpdateSequence(userId, projectDef)
val syncBegan = ProjectSynchronizationBegan(
syncId = newSyncId,
lastId = projectSyncData.lastId,
lastSync = projectSyncData.lastSync,
idSequence = updateSequence,
deletedIds = projectSyncData.deletedIds,
)
Result.success(syncBegan)
Expand Down Expand Up @@ -352,6 +354,17 @@ class ProjectRepository(
sessionManager.validateSyncId(userId, syncId, true)
}

private suspend fun getUpdateSequence(userId: Long, projectDef: ProjectDefinition): List<Int> {
val updateSequence = mutableSetOf<Int>()
updateSequence += sceneSynchronizer.getUpdateSequence(userId, projectDef)
updateSequence += sceneDraftSynchronizer.getUpdateSequence(userId, projectDef)
updateSequence += noteSynchronizer.getUpdateSequence(userId, projectDef)
updateSequence += timelineEventSynchronizer.getUpdateSequence(userId, projectDef)
updateSequence += encyclopediaSynchronizer.getUpdateSequence(userId, projectDef)

return updateSequence.toList()
}

companion object {
const val SYNC_DATA_FILE = "syncData.json"
const val ENTITY_DIR = "entities"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class ServerEncyclopediaSynchronizer(
)
}

override val entityType = ApiProjectEntity.Type.ENCYCLOPEDIA_ENTRY
override val entityClazz = ApiProjectEntity.EncyclopediaEntryEntity::class
override val pathStub = ApiProjectEntity.Type.ENCYCLOPEDIA_ENTRY.name.lowercase()
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ abstract class ServerEntitySynchronizer<T : ApiProjectEntity>(
protected val fileSystem: FileSystem,
protected val json: Json
) {
abstract val entityType: ApiProjectEntity.Type
abstract fun hashEntity(entity: T): String
protected abstract val entityClazz: KClass<T>
protected abstract val pathStub: String
Expand Down Expand Up @@ -123,6 +124,19 @@ abstract class ServerEntitySynchronizer<T : ApiProjectEntity>(
fileSystem.delete(path, false)
}

protected fun getEntityDefs(userId: Long, projectDef: ProjectDefinition): List<EntityDefinition> {
val entityDir = ProjectRepository.getEntityDirectory(userId, projectDef, fileSystem)
val entities = fileSystem.list(entityDir).mapNotNull{
parseEntityFilename(it)
}.filter { it.type == entityType }
return entities
}

open fun getUpdateSequence(userId: Long, projectDef: ProjectDefinition): List<Int> {
val entities = getEntityDefs(userId, projectDef)
return entities.map { it.id }
}

companion object {
val ENTITY_FILENAME_REGEX = Regex("^([0-9]+)-([a-zA-Z_]+).json$")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class ServerNoteSynchronizer(
)
}

override val entityType = ApiProjectEntity.Type.NOTE
override val entityClazz = ApiProjectEntity.NoteEntity::class
override val pathStub = ApiProjectEntity.Type.NOTE.name.lowercase()
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class ServerSceneDraftSynchronizer(
)
}

override val entityType = ApiProjectEntity.Type.SCENE_DRAFT
override val entityClazz = ApiProjectEntity.SceneDraftEntity::class
override val pathStub = ApiProjectEntity.Type.SCENE_DRAFT.name.lowercase()
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.darkrockstudios.apps.hammer.project.synchronizers

import com.darkrockstudios.apps.hammer.base.http.ApiProjectEntity
import com.darkrockstudios.apps.hammer.base.http.synchronizer.EntityHash
import com.darkrockstudios.apps.hammer.project.ProjectDefinition
import kotlinx.serialization.json.Json
import okio.FileSystem

Expand All @@ -20,6 +21,27 @@ class ServerSceneSynchronizer(
)
}

override fun getUpdateSequence(userId: Long, projectDef: ProjectDefinition): List<Int> {
val entities = getEntityDefs(userId, projectDef)

// Sort by SceneType, we want directories first
val entityIds = entities.mapNotNull { def ->
val entityResult = loadEntity(userId, projectDef, def.id)
return@mapNotNull if (entityResult.isSuccess) {
val entity = entityResult.getOrThrow()
Pair(def.id, entity.sceneType)
} else {
println("Failed to get entity $def.id: ${entityResult.exceptionOrNull()?.message}")
null
}
}
.sortedByDescending { it.second.ordinal }
.map { it.first }

return entityIds
}

override val entityType = ApiProjectEntity.Type.SCENE
override val entityClazz = ApiProjectEntity.SceneEntity::class
override val pathStub = ApiProjectEntity.Type.SCENE.name.lowercase()
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class ServerTimelineSynchronizer(
)
}

override val entityType = ApiProjectEntity.Type.TIMELINE_EVENT
override val entityClazz = ApiProjectEntity.TimelineEventEntity::class
override val pathStub = ApiProjectEntity.Type.TIMELINE_EVENT.name.lowercase()
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,12 @@ class ProjectRepositoryTest : BaseTest() {
encyclopediaSynchronizer = mockk()
sceneDraftSynchronizer = mockk()

coEvery { sceneSynchronizer.getUpdateSequence(userId, projectDefinition) } returns emptyList()
coEvery { noteSynchronizer.getUpdateSequence(userId, projectDefinition) } returns emptyList()
coEvery { timelineEventSynchronizer.getUpdateSequence(userId, projectDefinition) } returns emptyList()
coEvery { encyclopediaSynchronizer.getUpdateSequence(userId, projectDefinition) } returns emptyList()
coEvery { sceneDraftSynchronizer.getUpdateSequence(userId, projectDefinition) } returns emptyList()

val testModule = module {
single { fileSystem } bind FileSystem::class
single { Json } bind Json::class
Expand Down

0 comments on commit 282c4e8

Please sign in to comment.