Use Android-compatible archive durability barrier
This commit is contained in:
parent
4c330be8c3
commit
908c38546c
3 changed files with 11 additions and 31 deletions
|
|
@ -14,13 +14,8 @@ internal sealed interface ForegroundGenerationResult {
|
|||
}
|
||||
|
||||
/** Owns the bounded, user-initiated Android side of a generation conversation. */
|
||||
internal class SyncGenerationForegroundCoordinator(
|
||||
private val repository: TrainlogRepository,
|
||||
archiveDirectorySync: ((File) -> Unit)? = null,
|
||||
) {
|
||||
private val service =
|
||||
archiveDirectorySync?.let { SyncGenerationService(repository, it) }
|
||||
?: SyncGenerationService(repository)
|
||||
internal class SyncGenerationForegroundCoordinator(private val repository: TrainlogRepository) {
|
||||
private val service = SyncGenerationService(repository)
|
||||
|
||||
private fun requireExactKeys(value: JSONObject, expected: Set<String>, label: String) {
|
||||
val actual = value.keys().asSequence().toSet()
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
package com.labfytools.trainlog.data
|
||||
|
||||
import android.system.Os
|
||||
import android.system.OsConstants
|
||||
import java.io.File
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.charset.StandardCharsets
|
||||
|
|
@ -27,18 +25,7 @@ internal data class CapturedSyncGeneration(
|
|||
* CONTRACT: artifacts are immutable, bounded and published before the manifest marker. INVARIANT:
|
||||
* causal operation payloads are never rewritten to attach generation identity.
|
||||
*/
|
||||
internal class SyncGenerationService(
|
||||
private val repository: TrainlogRepository,
|
||||
private val syncArchiveDirectory: (File) -> Unit = { directory ->
|
||||
val descriptor =
|
||||
Os.open(directory.absolutePath, OsConstants.O_RDONLY or O_DIRECTORY, 0)
|
||||
try {
|
||||
Os.fsync(descriptor)
|
||||
} finally {
|
||||
Os.close(descriptor)
|
||||
}
|
||||
},
|
||||
) {
|
||||
internal class SyncGenerationService(private val repository: TrainlogRepository) {
|
||||
companion object {
|
||||
const val MAX_MANIFEST_BYTES = 64 * 1024
|
||||
const val MAX_ARTIFACTS = 32
|
||||
|
|
@ -48,10 +35,6 @@ internal class SyncGenerationService(
|
|||
const val MAX_PATH_BYTES = 240
|
||||
const val MAX_DIAGNOSTIC_BYTES = 1024
|
||||
const val MAX_RETAINED_PER_PEER = 8
|
||||
// Android's public OsConstants omits Linux O_DIRECTORY even though
|
||||
// Os.open forwards the platform flag. Keep the fixed Linux ABI value
|
||||
// local to the directory-fsync boundary.
|
||||
private const val O_DIRECTORY = 0x10000
|
||||
private val ID =
|
||||
Regex(
|
||||
"^(gen|peer|sy)_[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"
|
||||
|
|
@ -252,7 +235,13 @@ internal class SyncGenerationService(
|
|||
}
|
||||
if (!temporary.renameTo(destination))
|
||||
throw SyncGenerationException("cannot commit generation archive")
|
||||
syncArchiveDirectory(root)
|
||||
/* Android emulated external storage rejects directory fsync.
|
||||
* Reopen and sync the commit marker after the atomic rename so
|
||||
* the supported FUSE durability barrier covers the committed
|
||||
* namespace before the internal SQLite ledger can advance. */
|
||||
java.io.FileOutputStream(File(destination, "manifest.json"), true).use {
|
||||
it.fd.sync()
|
||||
}
|
||||
}
|
||||
validatePublished(destination, row[3])
|
||||
val checksum = archiveDigest(destination)
|
||||
|
|
|
|||
|
|
@ -101,11 +101,7 @@ class SyncDeploymentConversationTest {
|
|||
}
|
||||
val conversations = if (suppliedTransport == null) 24 else 1
|
||||
repeat(conversations) {
|
||||
// Robolectric cannot open a directory file descriptor. The
|
||||
// production default performs the real directory fsync; this
|
||||
// test substitutes only that kernel primitive while retaining
|
||||
// the production copy, validation, rename and ledger path.
|
||||
val coordinator = SyncGenerationForegroundCoordinator(repository) {}
|
||||
val coordinator = SyncGenerationForegroundCoordinator(repository)
|
||||
val executor = java.util.concurrent.Executors.newSingleThreadExecutor()
|
||||
val result =
|
||||
executor.submit<ForegroundGenerationResult> {
|
||||
|
|
|
|||
Loading…
Reference in a new issue