Skip to content

Commit c96887c

Browse files
committed
refactor: optimize folder deletion to be atomic and linear in time
1 parent 9fda53a commit c96887c

1 file changed

Lines changed: 44 additions & 31 deletions

File tree

src/main/kotlin/com/sakethh/linkora/data/repository/FoldersRepoImpl.kt

Lines changed: 44 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import com.sakethh.linkora.utils.getSystemEpochSeconds
1919
import kotlinx.serialization.encodeToString
2020
import kotlinx.serialization.json.Json
2121
import kotlinx.serialization.json.encodeToJsonElement
22-
import org.jetbrains.exposed.v1.core.SqlExpressionBuilder.eq
22+
import org.jetbrains.exposed.v1.core.SqlExpressionBuilder.inList
2323
import org.jetbrains.exposed.v1.core.and
2424
import org.jetbrains.exposed.v1.core.lowerCase
2525
import org.jetbrains.exposed.v1.jdbc.deleteWhere
@@ -102,45 +102,58 @@ class FoldersRepoImpl(private val panelsRepo: PanelsRepo) : FoldersRepo {
102102
override suspend fun deleteFolder(idBasedDTO: IDBasedDTO): Result<TimeStampBasedResponse> {
103103
return try {
104104
val eventTimestamp = getSystemEpochSeconds()
105-
when (val childFolders = getChildFolders(idBasedDTO)) {
106-
is Result.Failure -> {
107-
throw childFolders.exception
108-
}
109105

110-
is Result.Success -> {
111-
childFolders.response.map { it.id }.forEach { childFolderId ->
112-
panelsRepo.deleteAFolderFromAllPanels(
113-
IDBasedDTO(
114-
id = childFolderId,
115-
correlation = idBasedDTO.correlation,
116-
eventTimestamp = idBasedDTO.eventTimestamp
117-
)
118-
)
119-
transaction {
120-
FoldersTable.deleteWhere {
121-
FoldersTable.id.eq(childFolderId)
122-
}
123-
LinksTable.deleteWhere {
124-
idOfLinkedFolder.eq(childFolderId)
125-
}
126-
}
127-
deleteFolder(idBasedDTO.copy(id = childFolderId, eventTimestamp = eventTimestamp))
128-
}
106+
// i didn't use foreign keys back then, so welp, we got no other way to do this
107+
// 1. we gotta delete the folder and its references
108+
val foldersToDelete = mutableListOf<Long>()
109+
foldersToDelete.add(idBasedDTO.id)
110+
111+
// 2. delete its children and the underlying tree
112+
val childFoldersDeque = ArrayDeque<Folder>()
113+
114+
val childFolders = getChildFolders(idBasedDTO)
115+
if (childFolders is Result.Success) {
116+
childFoldersDeque.addAll(childFolders.response)
117+
}
118+
119+
while (childFoldersDeque.isNotEmpty()) {
120+
val currentFolder = childFoldersDeque.removeLast()
121+
122+
foldersToDelete.add(currentFolder.id)
123+
124+
val childFolders = getChildFolders(idBasedDTO.copy(id = currentFolder.id))
125+
if (childFolders is Result.Success) {
126+
childFoldersDeque.addAll(childFolders.response)
129127
}
130128
}
129+
131130
transaction {
132131
FoldersTable.deleteWhere {
133-
FoldersTable.id.eq(idBasedDTO.id)
132+
id.inList(foldersToDelete)
134133
}
135134
LinksTable.deleteWhere {
136-
idOfLinkedFolder.eq(idBasedDTO.id)
135+
idOfLinkedFolder.inList(foldersToDelete)
136+
}
137+
138+
PanelFoldersTable.deleteWhere {
139+
folderId.inList(foldersToDelete)
140+
}
141+
142+
for (folderId in foldersToDelete) {
143+
TombStoneHelper.insert(
144+
payload = Json.encodeToString(idBasedDTO.copy(eventTimestamp = eventTimestamp, id = folderId)),
145+
operation = Route.DELETE_FOLDER.name,
146+
eventTimestamp
147+
)
148+
149+
TombStoneHelper.insert(
150+
payload = Json.encodeToString(idBasedDTO.copy(eventTimestamp = eventTimestamp, id = folderId)),
151+
operation = Route.DELETE_A_FOLDER_FROM_ALL_PANELS.name,
152+
eventTimestamp
153+
)
137154
}
138-
TombStoneHelper.insert(
139-
payload = Json.encodeToString(idBasedDTO.copy(eventTimestamp = eventTimestamp)),
140-
operation = Route.DELETE_FOLDER.name,
141-
eventTimestamp
142-
)
143155
}
156+
144157
Result.Success(
145158
response = TimeStampBasedResponse(
146159
message = "Folder and its contents have been successfully deleted.", eventTimestamp = eventTimestamp

0 commit comments

Comments
 (0)