Skip to content

Commit

Permalink
fix the whole database (except backups) was corrupted and the applica…
Browse files Browse the repository at this point in the history
…tion cannot be reopened if the application is previously killed while writing files
  • Loading branch information
sunny-chung committed Jul 1, 2024
1 parent 26eb761 commit 47634c7
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import okio.buffer
import okio.sink
import okio.source
import java.io.File
import java.io.IOException
import java.io.OutputStream
import java.util.concurrent.ConcurrentHashMap

Expand Down Expand Up @@ -41,15 +42,24 @@ class FileManager {

suspend fun writeToFile(file: File, writeOperation: (OutputStream) -> Unit) {
withLock(file) {
file.sink().buffer().use {
it.write(magicBytes)
it.write(byteArrayOf(separatorByte))
it.write(fileSchemaVersion.toString().toByteArray())
it.write(byteArrayOf(separatorByte))
file.outputStream().use { fileOutputStream ->
val fileLock = fileOutputStream.channel.tryLock()
?: throw IOException("Cannot lock file ${file.absolutePath} for writing")

try {
file.sink().buffer().use {
it.write(magicBytes)
it.write(byteArrayOf(separatorByte))
it.write(fileSchemaVersion.toString().toByteArray())
it.write(byteArrayOf(separatorByte))

val outputStream = it.outputStream().buffered()
writeOperation(outputStream)
outputStream.flush() // must
val outputStream = it.outputStream().buffered()
writeOperation(outputStream)
outputStream.flush() // must
}
} finally {
fileLock.release()
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.KSerializer
import kotlinx.serialization.cbor.Cbor
import java.io.File
import java.io.IOException
import java.util.concurrent.ConcurrentHashMap

@OptIn(ExperimentalSerializationApi::class)
Expand Down Expand Up @@ -53,11 +54,21 @@ class PersistenceManager {
// file = file,
// content = bytes
// )
val tmpFile = File(file.parentFile, "${file.name}.tmp")
if (tmpFile.exists() && !tmpFile.canWrite()) {
throw IOException("File ${tmpFile.absolutePath} is not writeable")
}
fileManager.writeToFile(
file = file,
file = tmpFile,
) { outStream ->
codecCustomizedWriter.encodeToStream(serializer, document, outStream)
}
if (file.exists() && !file.delete()) {
throw IOException("File ${file.absolutePath} cannot be deleted for new content")
}
if (!tmpFile.renameTo(file)) {
throw IOException("File ${tmpFile.absolutePath} cannot be renamed to ${file.name}")
}
}

internal suspend inline fun <T> readFile(relativePath: String, serializer: KSerializer<T>): T? {
Expand Down

0 comments on commit 47634c7

Please sign in to comment.