재실행 시 영상 재처리로 인한 멈춤 완화 — ffmpeg/ffprobe 저우선순위 + 백업 하루 1회
증상: 영상이 많은 라이브러리에서 첫 로딩이 오래 걸리고, 다 불러온 뒤 재실행하면 멈춤. 원인은 미완료 영상 썸네일 백로그가 시작 시 enqueue_pending으로 재개되는데, ffmpeg/ffprobe가 '자식 프로세스'라 스레드 저우선순위(이전 수정)를 상속받지 못해 NORMAL로 전 코어를 포화시켜 WebView UI가 멈춘 것. (재실행 시 전체 재스캔은 없음 — 자동스캔은 dev 전용, 워처도 시작 시 스캔 안 함. 재처리는 미완료 썸네일 재개일 뿐.) - ffmpeg::command(): Windows CREATE_NO_WINDOW에 BELOW_NORMAL_PRIORITY_CLASS 추가, Unix는 pre_exec로 setpriority(nice+10). 검증: 스캔 중 ffmpeg/ffprobe PriorityClass=BelowNormal 관측(ffmpeg 406, ffprobe 46 샘플). - 시작 유지보수 스레드도 저우선순위로, DB 백업(VACUUM INTO=전체 복제)은 24시간 내 백업 있으면 건너뜀 — 대용량 라이브러리 재실행 부하/디스크 낭비 감소. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -81,16 +81,31 @@ fn which(name: &str) -> Option<PathBuf> {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 콘솔 창 없이 실행되는 Command 생성 (Windows CREATE_NO_WINDOW)
|
/// 콘솔 창 없이 + 저우선순위로 실행되는 Command 생성.
|
||||||
|
/// ffmpeg/ffprobe는 CPU 집약적이라 자식 프로세스를 저우선순위로 띄워야
|
||||||
|
/// (스레드 우선순위는 자식에 상속되지 않음) 영상 다수 처리 중에도 UI가 멈추지 않는다.
|
||||||
pub fn command(path: &Path) -> Command {
|
pub fn command(path: &Path) -> Command {
|
||||||
let cmd = Command::new(path);
|
let mut cmd = Command::new(path);
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
let cmd = {
|
{
|
||||||
use std::os::windows::process::CommandExt;
|
use std::os::windows::process::CommandExt;
|
||||||
let mut c = cmd;
|
// CREATE_NO_WINDOW(0x0800_0000) | BELOW_NORMAL_PRIORITY_CLASS(0x0000_4000)
|
||||||
c.creation_flags(0x0800_0000);
|
cmd.creation_flags(0x0800_0000 | 0x0000_4000);
|
||||||
c
|
}
|
||||||
};
|
#[cfg(unix)]
|
||||||
|
{
|
||||||
|
use std::os::unix::process::CommandExt;
|
||||||
|
// fork 후 exec 전에 자식 니스값을 올린다(우선순위 하향). setpriority(PRIO_PROCESS,self,10).
|
||||||
|
unsafe {
|
||||||
|
cmd.pre_exec(|| {
|
||||||
|
extern "C" {
|
||||||
|
fn setpriority(which: i32, who: u32, prio: i32) -> i32;
|
||||||
|
}
|
||||||
|
setpriority(0, 0, 10);
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
cmd
|
cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+11
-1
@@ -187,8 +187,18 @@ fn main() {
|
|||||||
std::thread::Builder::new()
|
std::thread::Builder::new()
|
||||||
.name("maintenance".into())
|
.name("maintenance".into())
|
||||||
.spawn(move || {
|
.spawn(move || {
|
||||||
|
// 유지보수는 백그라운드 — 시작 시 UI를 방해하지 않도록 저우선순위.
|
||||||
|
archive_indexer::prio::set_current_thread_low();
|
||||||
let backup = data_dir.join("archive.db.bak");
|
let backup = data_dir.join("archive.db.bak");
|
||||||
if let Err(e) = archive_indexer::maintenance::backup_db(&db, &backup) {
|
// 백업(VACUUM INTO = DB 전체 복제)은 하루 한 번만 — 대용량 라이브러리에서
|
||||||
|
// 매 실행 백업은 시작을 느리게 하고 디스크를 낭비한다.
|
||||||
|
let backup_fresh = std::fs::metadata(&backup)
|
||||||
|
.and_then(|m| m.modified())
|
||||||
|
.map(|t| t.elapsed().map(|e| e.as_secs() < 24 * 3600).unwrap_or(false))
|
||||||
|
.unwrap_or(false);
|
||||||
|
if backup_fresh {
|
||||||
|
tracing::debug!("최근(24h 내) 백업 존재 — 이번 실행은 백업 건너뜀");
|
||||||
|
} else if let Err(e) = archive_indexer::maintenance::backup_db(&db, &backup) {
|
||||||
tracing::warn!("DB 백업 실패: {e}");
|
tracing::warn!("DB 백업 실패: {e}");
|
||||||
}
|
}
|
||||||
let cutoff = now_ms() - 30 * 24 * 3600 * 1000;
|
let cutoff = now_ms() - 30 * 24 * 3600 * 1000;
|
||||||
|
|||||||
Reference in New Issue
Block a user