ANDREW/
選單觀點
中文EN
← 回到文章庫

TECHNOLOGY / BACKUP / RESTORE / RETENTION

符合清理條件,就能安全刪除嗎?

Andrew Yuan · 技術實驗

建立備份:檔案存下來,還拿得回來嗎?

想像公司每天備份,報告全是綠燈。直到資料出問題,主管說:「趕快還原!」工程師回答:「備份有做,Restore 還沒測過。」

這有點像每天檢查保險箱的門有沒有鎖好,等到需要拿東西,才開始找鑰匙。備份建立成功,還需要一條走得完的取回路徑。

我做的 Keepweft 是本地 SQLite 備份與驗證還原的實驗性 Controller。目前範圍是一個已註冊的 WAL-mode 資料庫、固定的 ledger-v1 示範資料模型,以及一份受管理的唯讀還原副本。排程備份、依時間保留與正式資料庫切換,仍不在已完成能力裡。

Keepweft 概念插圖:黃銅文件架上的紙夾與問號標籤,旁邊是空托盤;符合清理條件,仍要確認依賴與刪除權限。
概念插圖:文件架與問號呈現保留依賴,並非實際管理介面。

它透過 SQLite Online Backup API,在期限與資源限制內,把讀取快照存成獨立備份,再驗證固定應用契約並記錄在持久化的備份目錄(Catalog)中。來源可以繼續有新交易;備份要能說清楚自己保存的是哪一個快照,而不是把後來的來源狀態混進來。長時間持有讀取快照也可能讓來源 WAL 成長,取消與期限處理因此也是流程的一部分。

驗證還原:SQLite 說沒壞,應用也能用嗎?

Restore 會另外建立一份副本,不取代正在使用的來源資料庫。檢查涵蓋資料庫完整性、身分、內容與固定的應用關聯;受管理的 Reader(讀取副本的程序)再以唯讀方式實際查詢這份副本。這個流程能檢查指定示範應用的結果,不等於完成正式服務 Cutover。

前期 review 找到過一個真正的漏抓:子項目找不到有效的父交易,卻仍通過原本的驗證。SQLite 允許子外鍵是 NULL;只看 foreign_key_check、總筆數與分組形狀,無法獨立證明每個項目都屬於一筆真實交易。

修正後,驗證器另外檢查每個項目是否對應到有效交易;正式 Controller 與 Worker 的負向測試必須拒絕這些資料,且不能留下已發布備份。這裡的「Validated」有具體契約:它驗的是這份 ledger 的規則,沒有替所有 SQLite Schema 簽一張通用合格證。

備份請求另有持久化去重。客戶端保留原請求封套,才能辨識同一請求的重送;收到過期權限回覆,也不能自行換一個新請求識別,假裝是第一次。Reader 的狀態變更則使用目前的執行世代與狀態版本(epoch/revision)權限檢查,不能把備份去重當成所有操作都具相同語意。

使用副本:不知道退出了沒有,就繼續保留

受管理 Reader 可能在 Controller 崩潰後繼續讀取。此時找不到原本的控制回應,不能當作 Reader 已經停止;Keepweft 透過實際跨程序的占用鎖(guard)判斷占用。Reader 仍存活,或占用尚未釐清,就保留還原副本,並維持原備份的 Pin。

Pin 是這個管理流程裡的保留依賴:副本還不能安全釋放,它依賴的備份也不能交給保留政策清掉。這項保護限於受管理流程與 guard,不會自動發現任意外部程式開啟檔案的行為。

停止要求送出後,還要證明 Reader 確實退出。逾時、暫停或忙碌的 guard 都不能直接變成成功;重啟後也不會只憑磁碟上的舊 PID 就殺程序,因為 PID 可能已被重用。Controller 正常停止時,也刻意保留仍在運作的 Reader;完整停機需要先處理 Reader,再停止 Controller。

當退出與占用證據允許,才能釋放副本、完成刪除與持久化步驟,再解除備份依賴。流程中斷後,Recovery 必須對帳;看到檔案消失或狀態文字改變,還不足以單獨宣稱整段釋放完成。

清理備份:保留政策還要等哪些條件?

依數量保留,回答的是「想保留多少份」。是否能刪除某一份,還需要目前的依賴、最新備份保護、權屬與狀態證據。符合政策的候選,不會因此取得跳過這些條件的權限。

窄螢幕可左右滑動表格,查看完整內容。

從保存到清理,需要不同的證據
目前知道什麼下一步可以做什麼仍要保留的限制
備份通過固定應用契約建立獨立還原副本並驗證。不代表正式應用已切換。
Reader 仍存活或占用不明保留副本與備份 Pin,釐清占用。不能以不回應推定已退出。
Reader 退出與 guard 空閒得到確認在目前權限與狀態允許下釋放副本。停止要求本身不等於退出證據。
副本完成釋放,備份已解除 Pin重新評估是否符合清理條件。最新備份與其他保護仍然適用。

保守保留有成本。被 Pin 住的舊備份可能占住名額或容量,使新備份被拒絕。Keepweft 保護最新與被 Pin 的備份;若無法取得安全名額,就不假裝容量已經釋放。部署者需要決定能接受多久沒有新快照,以及誰負責解除阻塞。

管理空間也有範圍:邏輯容量預算是軟體政策,不是硬碟配額,來源資料庫與它的 WAL/SHM 不在受管理預算裡。請求收據(receipt)、Catalog 與 guard 等資訊會留下;目前沒有通用的線上 guard/tombstone 自動清理。不能為了騰空間直接刪 guard,否則連判斷誰還在使用的依據也會一起消失。

保留的驗證:83 項、24 次循環與兩次重開機

以下引用既有 run 21c52afcecce99d5,文章製作沒有重跑 VM 或硬體。macOS 與隔離 Linux VM 各通過相同的 83 項測試,無失敗、錯誤或跳過;Linux 使用實際安裝的 wheel,並核對來源模組與 SQLite runtime。另有 10 個既有 Reader/故障案例、4 個 closeout 案例與兩次正常 VM reboot;63 個報告竄改控制被拒絕,這些是驗收器的負向控制,不是 63 次產品故障。

24 次 Backup/Restore/Reader 生命週期在同一 Controller Process 中完成,歷時 124.69 秒。每輪至少有 5 秒活躍 Reader 停留與前後查詢;輪次邊界的子程序、活躍還原、Reader 與 Pin 都歸零。

這段觀察並非零成長:Controller 取樣 RSS 從 21,096 KiB,經四輪宣告暖機到 28,424 KiB,最後/最大樣本為 30,244 KiB;暖機後增加 1,820 KiB,在預先設定的 4,096 KiB 額度內。原始與靜止後 FD 樣本均為 7。這是短期 Linux VM 取樣,不能當連續峰值、無洩漏或長期資源上限;保留的 metadata 也依預期成長。

Receipt 與邏輯容量拒絕案例另外確認:拒絕新工作時,備份檔案、完整 Catalog 與 receipt 內容維持不變,既有 query/stop/release 仍能執行。較早一次資源檢查失敗沒有保存出錯樣本,確切原因仍未知;目前這次通過不會補出那次缺少的答案。

兩次重開機前,先終止 Controller,避免正常關閉替測試完成 Recovery。開機後、任何 Controller 啟動前,先確認 Catalog 與副本內容保留不變,再啟動服務,分別對帳原本正在提供讀取的副本與中斷的釋放。這支持的是「開機後明確啟動服務」的受測流程,不是 systemd 自動啟動或開機零空窗保證。

同一台設備,仍然是同一個故障範圍

想像公司很自豪:「重要文件有三份備份。」
「放在哪裡?」
「同一個檔案櫃。」
「那檔案櫃失火呢?」
主管想了一下:「至少備份數量成長了 200%。」

數量算對了,設備故障的風險仍然在。Keepweft 的本地副本不保護設備損壞、遺失或整個儲存範圍消失。離機或遠端備份、加密、Point-in-Time Recovery、任意 Schema、實體儲存行為、突然斷電與正式資料庫 Cutover 都未在這次範圍完成驗證;正常 VM reboot 不能代替它們。

這也是受信任同 UID 的本地單 Controller 設計,不是對抗惡意同使用者程序的隔離環境。不能把 ownership guard 當作所有攻擊情境下的防刪除機制。

帶回部署決策:誰能判斷現在可以釋放?

我會先和維運團隊約定:要恢復的是哪種讀取能力、哪個資料版本;Reader 未退出或新備份被容量政策擋住時,要由誰調查;以及無法在線上釐清時,如何保留現況並轉入離線處理。

這些決定要有可執行的接手路徑。確認誰能檢查程序與 guard、誰有權停止服務,以及哪些資料和磁碟預算必須另外監測;也要算入保守保留占用的空間、較舊快照持續被使用的風險與維護成本。把狀態改成綠色,無法取代這些安排。

對 Keepweft 而言,安全清理需要把建立、驗證、使用、釋放與保留政策接起來。符合清理條件之後,仍要有當下足夠的證據,才有權執行刪除。

來源與保留紀錄

分享這篇