Fix session save freeze and restore losing removed files

Save freeze fix:
- Added record_symlinks_batch() that inserts all symlinks in a single
  DB transaction instead of opening a new connection per file
- _save_session and _auto_save_session now use batch inserts
- With 1700 files this goes from 1700 connection cycles to 1

Removed files fix:
- _restore_files_from_session now filters by _removed_files so
  individually deleted files stay removed even when restoring from
  session data that pre-dates the removal

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-12 12:06:01 +01:00
parent c03ee0665b
commit 0efc6e153f
2 changed files with 47 additions and 17 deletions

View File

@@ -216,6 +216,31 @@ class DatabaseManager:
except sqlite3.Error as e:
raise DatabaseError(f"Failed to record symlink: {e}") from e
def record_symlinks_batch(
self,
session_id: int,
records: list[tuple[str, str, str, int]],
) -> None:
"""Record multiple symlinks in a single transaction.
Args:
session_id: The session these symlinks belong to.
records: List of (source, link, filename, seq) tuples.
Raises:
DatabaseError: If recording fails.
"""
try:
with self._connect() as conn:
conn.executemany(
"""INSERT INTO symlinks
(session_id, source_path, link_path, original_filename, sequence_number)
VALUES (?, ?, ?, ?, ?)""",
[(session_id, src, lnk, fname, seq) for src, lnk, fname, seq in records]
)
except sqlite3.Error as e:
raise DatabaseError(f"Failed to record symlinks: {e}") from e
def get_sessions(self) -> list[SessionRecord]:
"""List all sessions with link counts.

View File

@@ -3166,17 +3166,18 @@ class SequenceLinkerUI(QWidget):
self._save_session_settings(session_id, save_effective_types=True)
# Save the file list so the exact sequence can be restored
records = []
for i, (source_dir, filename, folder_idx, file_idx) in enumerate(files):
source_path = source_dir / filename
ext = source_path.suffix
link_name = f"seq{folder_idx + 1:02d}_{file_idx:04d}{ext}"
self.db.record_symlink(
session_id=session_id,
source=str(source_path.resolve()),
link=str(Path(dest) / link_name),
filename=filename,
seq=i,
)
records.append((
str(source_path.resolve()),
str(Path(dest) / link_name),
filename,
i,
))
self.db.record_symlinks_batch(session_id, records)
except Exception:
pass # Best-effort save on close
@@ -3200,19 +3201,20 @@ class SequenceLinkerUI(QWidget):
self._save_session_settings(session_id, save_effective_types=True)
# Save the exact file list
# Save the exact file list in a single transaction
files = self._get_files_in_order()
records = []
for i, (source_dir, filename, folder_idx, file_idx) in enumerate(files):
source_path = source_dir / filename
ext = source_path.suffix
link_name = f"seq{folder_idx + 1:02d}_{file_idx:04d}{ext}"
self.db.record_symlink(
session_id=session_id,
source=str(source_path.resolve()),
link=str(Path(dest) / link_name),
filename=filename,
seq=i,
)
records.append((
str(source_path.resolve()),
str(Path(dest) / link_name),
filename,
i,
))
self.db.record_symlinks_batch(session_id, records)
main_count = sum(
1 for i, f in enumerate(self.source_folders)
@@ -3667,8 +3669,11 @@ class SequenceLinkerUI(QWidget):
# Sort files by their sequence index
sorted_files = sorted(file_list, key=lambda x: x[0])
# Folder existence already verified in _try_resume_session;
# only recheck individual files if the folder has changed on disk.
# Filter out individually removed files
removed = self._removed_files.get(folder_path, set())
if removed:
sorted_files = [(idx, fname) for idx, fname in sorted_files if fname not in removed]
if not sorted_files:
continue