Performance
This commit is contained in:
parent
923835203a
commit
44656c81d5
3 changed files with 45 additions and 10 deletions
|
|
@ -17,12 +17,29 @@ class BackupEntry(
|
|||
val sizeBytes: Long by lazy { sizeComputer() }
|
||||
}
|
||||
|
||||
data class BackupMetrics(val fileCount: Int, val sizeBytes: Long)
|
||||
|
||||
object BackupHistory {
|
||||
|
||||
// Used internally for parsing — java.time formatter since kotlinx.datetime
|
||||
// doesn't have custom format patterns built in
|
||||
private val JAVA_TIMESTAMP_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH-mm-ss")
|
||||
|
||||
/**
|
||||
* Counts backups without parsing timestamps or sorting — just matches the naming pattern.
|
||||
*/
|
||||
fun countBackups(backupDir: File): Int {
|
||||
if (!backupDir.exists() || !backupDir.isDirectory) return 0
|
||||
return backupDir.listFiles()?.count { file ->
|
||||
val name = file.nameWithoutExtension
|
||||
val isZip = file.extension.equals("zip", ignoreCase = true) && file.isFile
|
||||
val isDir = file.isDirectory
|
||||
if (!isZip && !isDir) return@count false
|
||||
try { java.time.LocalDateTime.parse(name, JAVA_TIMESTAMP_FORMAT); true }
|
||||
catch (_: DateTimeParseException) { false }
|
||||
} ?: 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists all existing backups in the backup directory, sorted newest first.
|
||||
*/
|
||||
|
|
@ -37,13 +54,18 @@ object BackupHistory {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the file count for a backup entry without computing its full size.
|
||||
* Computes file count and size in a single pass over the backup tree.
|
||||
*/
|
||||
fun fileCount(entry: BackupEntry): Int {
|
||||
fun computeMetrics(entry: BackupEntry): BackupMetrics {
|
||||
return if (entry.isCompressed) {
|
||||
java.util.zip.ZipFile(entry.path).use { it.size() }
|
||||
java.util.zip.ZipFile(entry.path).use { zip ->
|
||||
BackupMetrics(zip.size(), entry.path.length())
|
||||
}
|
||||
} else {
|
||||
entry.path.walkTopDown().count { it.isFile }
|
||||
var count = 0
|
||||
var size = 0L
|
||||
entry.path.walkTopDown().filter { it.isFile }.forEach { count++; size += it.length() }
|
||||
BackupMetrics(count, size)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -46,6 +46,19 @@ object BackupScheduler {
|
|||
if (schedulerJob?.isActive == true) return
|
||||
logger.info { "Backup scheduler started" }
|
||||
|
||||
// Eagerly set timestamps so StatusScreen has data immediately.
|
||||
// Only set lastBackupTime and nextBackupTime here (cheap).
|
||||
// The full lastBackupResult (fileCount, sizeBytes) is filled by the async path
|
||||
// since fileCount() walks the directory tree and would block the main thread.
|
||||
val config = ConfigManager.config.value
|
||||
if (config.isConfigured && config.backupPath != null) {
|
||||
val backups = BackupHistory.listBackups(File(config.backupPath))
|
||||
if (backups.isNotEmpty()) {
|
||||
_state.update { it.copy(lastBackupTime = backups.first().timestamp) }
|
||||
}
|
||||
updateNextBackupTime(TimeZone.currentSystemDefault())
|
||||
}
|
||||
|
||||
schedulerJob = scope.launch {
|
||||
// Collect progress from BackupEngine
|
||||
launch {
|
||||
|
|
@ -65,23 +78,23 @@ object BackupScheduler {
|
|||
}
|
||||
|
||||
// On first config emission, restore last backup info from disk
|
||||
if (_state.value.lastBackupTime == null && config.backupPath != null) {
|
||||
if (_state.value.lastBackupResult == null && config.backupPath != null) {
|
||||
val backups = BackupHistory.listBackups(File(config.backupPath))
|
||||
if (backups.isNotEmpty()) {
|
||||
val latest = backups.first()
|
||||
val fileCount = BackupHistory.fileCount(latest)
|
||||
val metrics = BackupHistory.computeMetrics(latest)
|
||||
_state.update {
|
||||
it.copy(
|
||||
lastBackupTime = latest.timestamp,
|
||||
lastBackupResult = BackupResult.Success(
|
||||
backupPath = latest.path,
|
||||
fileCount = fileCount,
|
||||
sizeBytes = latest.sizeBytes,
|
||||
fileCount = metrics.fileCount,
|
||||
sizeBytes = metrics.sizeBytes,
|
||||
durationMs = 0,
|
||||
),
|
||||
)
|
||||
}
|
||||
logger.info { "Restored last backup info from disk: ${latest.path.name} ($fileCount files)" }
|
||||
logger.info { "Restored last backup info from disk: ${latest.path.name} (${metrics.fileCount} files)" }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ class StatusViewModel : ViewModel() {
|
|||
BackupScheduler.state.map { it.lastBackupResult }.distinctUntilChanged(),
|
||||
) { backupPath, _ ->
|
||||
if (backupPath != null) {
|
||||
BackupHistory.listBackups(File(backupPath)).size
|
||||
BackupHistory.countBackups(File(backupPath))
|
||||
} else {
|
||||
0
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue