Files
ComfyAssets-ComfyUI_PromptM…/utils/history_read_fence.py
T
Vito 4c9cb7f5ea Cleanup: dead code, small fixes, Library sorts, old-UI links (#620) (#621)
* refactor: remove dead and test-only code and the write-only prompt registry (#620)

Removes helpers nothing calls, test-only wrappers and their tests, the unused
config writer, three unused images, and the prompt registry the nodes wrote to
but nothing read, which grew with every run.

* fix: network-share readers, model unload off the loop, bad save requests (#620)

Every read-only SQLite open now uses one URI helper, so a library on a Windows
network share works in backups, restores, moves and history reads. Unloading a
model no longer blocks the server. A malformed /save body gets a 400, not a 500.

* feat(library): add Recently used, Most used and Name Z-A sorting (#620)

The old Dashboard's sorts the community asked for, on the Prompt library, in
saved views, and with paging through the whole list.

* feat(workspace): stop linking the old pages (#620)

They show a removal notice since 3.5.1. System no longer links them, the load
failure offers a reload and ComfyUI instead, and the README says they are going
away. The pages still work at their URLs.
2026-11-02 19:00:30 +00:00

80 lines
2.7 KiB
Python

"""Restore publication checks for snapshot-owned history reads."""
import os
import sqlite3
import threading
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from ..database.operations import PromptDatabase
try:
from ..database.sqlite_uri import readonly_uri
from ..database.execution_history import (
HistoryEpochChanged,
subscribe_history_restore,
)
except ImportError:
from database.sqlite_uri import readonly_uri
from database.execution_history import (
HistoryEpochChanged,
subscribe_history_restore,
)
class HistoryReadFence:
"""Combine a fresh storage epoch with process-local restore notifications."""
def __init__(self, db: "PromptDatabase") -> None:
self._path = os.path.normcase(os.path.realpath(db.model.db_path))
self._lock = threading.Lock()
self._generation = 0
self._closed = False
self._unsubscribe = subscribe_history_restore(db.model.db_path, self._restored)
def _restored(self) -> None:
with self._lock:
if not self._closed:
self._generation += 1
def _check(self, generation: int | None = None, epoch: str = "") -> int:
if self._closed:
raise sqlite3.OperationalError("History reads are closed")
if generation is not None and generation != self._generation:
raise HistoryEpochChanged(epoch)
return self._generation
def begin(self) -> int:
with self._lock:
return self._check()
def validate(self, epoch: str, generation: int) -> None:
"""Validate after the caller's read transaction and response work ended."""
with self._lock:
self._check(generation, epoch)
# An API restore retires its previous model. Never reopen that owner,
# and never reuse the projection transaction's view of the epoch.
conn = sqlite3.connect(readonly_uri(self._path), uri=True, timeout=0.25)
try:
row = conn.execute(
"SELECT storage_epoch FROM execution_history_metadata "
"WHERE singleton_id=1"
).fetchone()
if row is None or not isinstance(row[0], str):
raise sqlite3.DatabaseError("Missing history storage epoch")
current_epoch = row[0]
finally:
conn.close()
with self._lock:
self._check(generation, epoch)
if current_epoch != epoch:
raise HistoryEpochChanged(current_epoch)
def close(self) -> None:
with self._lock:
if self._closed:
return
self._closed = True
self._generation += 1
self._unsubscribe()