者-讀者改造:以同步引擎?zhèn)}庫為只讀副本、以隊(duì)列暫存寫入)
CLI【免費(fèi)下載鏈接】himalayaCLI to manage emails項(xiàng)目地址https://gitcode.com/gh_mirrors/hi/himalaya點(diǎn)擊查看免費(fèi)下載這篇技術(shù)指南圍繞 Himalaya 的 pimdir 后端展開它把本地 pimdir 倉庫定位為同步引擎Neverest擁有的離線副本Himalaya 在其中只扮演讀者reader與生產(chǎn)者producer絕不擔(dān)任倉庫的 owner。文中將剖析這一角色重定位背后的兩個(gè)真實(shí)缺陷郵箱按內(nèi)部 key 命名、寫入觸發(fā) owner 的垃圾回收導(dǎo)致數(shù)據(jù)破壞、對應(yīng)的修復(fù)方案open_read_only無鎖讀取、PimdirProducer隊(duì)列寫入、pimdir.account/pimdir.namespace配置語義以及從源碼與測試中可驗(yàn)證的完整實(shí)現(xiàn)細(xì)節(jié)。讀完你可以理解 pimdir 后端每個(gè)讀寫操作在倉庫中的真實(shí)調(diào)用鏈并能正確配置與使用himalaya pimdir queue命令族。本文基于倉庫中的變更提案 cairn/changes/pimdir-producer-reader/proposal.md狀態(tài)landed即已落地及其 delta.md、tasks.md并對照 src/pimdir/backend.rs、src/pimdir/client.rs、src/config.rs 與 config.sample.toml 中的實(shí)現(xiàn)展開。背景pimdir 后端的兩個(gè)真實(shí)缺陷pimdir 是一個(gè)本地離線緩存式后端倉庫本身是SQLite 索引 內(nèi)容尋址 blob見 src/config.rs 中PimdirConfig的文檔注釋由 Neverest 同步引擎填充Himalaya 讀取它來離線看郵件、暫存編輯動作。在本次變更pimdir-producer-reader之前這個(gè)后端存在兩個(gè)缺陷一個(gè)可見、一個(gè)不可見。缺陷一郵箱按倉庫內(nèi)部 key 命名。Neverest 以namespace/name形式為 hub collection 命名因此服務(wù)器稱為INBOX的郵箱在倉庫里實(shí)際存為imap/INBOX。舊實(shí)現(xiàn)把這條 id 原樣當(dāng)作顯示名并在輸入時(shí)也期望收到完整 id于是mailbox list會把同一個(gè) id 打印兩次既是 key 又當(dāng)名字-m INBOX查的是一個(gè)從未寫入過任何內(nèi)容的 collection得到的是空的信封表而且靜默無錯誤mailbox.alias.inbox和未指定郵箱時(shí)隱式用 INBOX的默認(rèn)邏輯對所有 pimdir 賬戶全部失效——因?yàn)閮烧叨冀馕鰹槁忝鸌NBOX。缺陷二寫入走了 owner 的寫路徑觸發(fā)整庫垃圾回收。在 io-pimdir 0.2 上store_flags及其余四個(gè)寫操作都驅(qū)動一個(gè) io-replica 的mutate協(xié)程并調(diào)用ReplicaStorage::write而 0.2 版本會在每個(gè)批次結(jié)束時(shí)執(zhí)行collect_garbageDELETE FROM objects WHERE refcount 0并 unlink 那些 blob。Neverest 的水合hydration階段會把郵件正文以refcount 0流式寫入 blob 樹并在稍后的階段才把它們掛到對象上——pimdir SPEC §14 明確允許這種暫留未掛接狀態(tài)。結(jié)果就是同步進(jìn)行中執(zhí)行一次himalaya flag add就會摧毀所有尚未掛接的正文連字節(jié)一起刪掉。更糟的是 io-pimdir 0.2 不持有 owner 鎖兩個(gè)進(jìn)程之間沒有任何互斥。這份被讀取的倉庫以 GB 計(jì)而同步正是給它重新灌水的過程。Himalaya 從來就不是這份倉庫的 owner也不該扮演這個(gè)角色它讀的是副本、暫存的是意圖。格式規(guī)范早已寫明——讀者不拿鎖生產(chǎn)者拿共享鎖并排隊(duì)入隊(duì)動作由 owner 消費(fèi)。本次變更就是讓實(shí)現(xiàn)與這一角色定位對齊。角色重定位Himalaya 是讀者與生產(chǎn)者絕不是 owner變更的核心原則delta 中表述為 requirementpimdir is a reader and a producer, never the owner包含三層把倉庫當(dāng)作可能是部分緩存的副本。get_message遇到本地沒有正文level Full、無存儲對象的消息時(shí)必須報(bào)告清晰的正文未拉取body not fetched狀態(tài)——這是觸發(fā)同步的提示——而不是報(bào)數(shù)據(jù)丟失錯誤這條消息仍然出現(xiàn)在列表中。對應(yīng)實(shí)現(xiàn)見 src/pimdir/backend.rs 的get_message其錯誤信息為Message \{id} in {mailbox} is not downloaded yet (body not fetched); run a sync to hydrate it。讀取以只讀方式打開倉庫、不拿鎖。這樣同步進(jìn)行中既不會阻塞 Himalaya也不會被 Himalaya 阻塞。在 src/pimdir/client.rs 的PimdirClient::new中可以看到客戶端用PimdirReader::open(root)打開倉庫——這是無鎖的 reader 角色——并且要求pimdir.db必須已存在如果路徑下沒有倉庫直接報(bào)錯提示檢查pimdir.root并運(yùn)行一次同步來創(chuàng)建它而不是替你新建一個(gè)空倉庫來掩蓋拼錯路徑。寫入一律通過生產(chǎn)者句柄暫存為排隊(duì)的PimdirAction由倉庫 owner 在下一次運(yùn)行時(shí)應(yīng)用并推送。后端不寫索引、不加載 collection、不執(zhí)行 owner 的對象清掃——清掃與同步并行會摧毀同步已流式寫入但尚未掛接的正文。在實(shí)現(xiàn)層面寫操作到動作的映射如下見 src/pimdir/backend.rs后端方法動作語義store_flagsSetFlags { seq, flags }全量替換標(biāo)記集重復(fù)應(yīng)用結(jié)果一致add_messageAdd { link_id, flags, object }暫存一條本地起草的消息等待上傳copy_messagesCopy { seq, to }下次同步的服務(wù)器端復(fù)制不重復(fù)上傳正文move_messagesMove { seq, to }下次同步的服務(wù)器端移動delete_messagesRemove { seq }下次同步推送給后端的刪除send_messageUnknown { kind: submit, payload }排隊(duì)一條待發(fā)送消息owner 持憑證發(fā)送動作以公開的seq尋址每執(zhí)行一次操作就enqueue一次。生產(chǎn)者句柄是按暫存批次打開的PimdirClient::producer()每次調(diào)用PimdirProducer::open并以固定名himalaya記錄生產(chǎn)者身份用完即釋放Himalaya 從不長時(shí)間持有可能排干隊(duì)列或清掃倉庫的句柄。send_message的 payload 以版本化 JSON{v: 1, object, from, rcpts, subject}編碼由 owner 解碼執(zhí)行正文中保留Bcc:字段發(fā)送通道會移除它Graph 依賴它推導(dǎo)收件人——這些細(xì)節(jié)都有a_sent_message_is_one_submit_row_with_its_envelope等測試用例在 src/pimdir/backend.rs 中驗(yàn)證。先落 blob、再入隊(duì)隊(duì)列行是釘住正文的錨Add與submit動作引用一個(gè)正文對象。實(shí)現(xiàn)的順序有嚴(yán)格要求add_message/send_message中可見對原始字節(jié)求內(nèi)容哈希self.blobs.hash(raw)用PimdirBlobWriter把字節(jié)持久化寫入 blob 樹write_blob中writer.commit(hash)構(gòu)造動作并把Some(object)隨動作一起enqueue。由于動作入隊(duì)時(shí)正文已經(jīng)落盤、且隊(duì)列行釘住了該對象owner 在應(yīng)用動作前絕不會清掃它——正文先寫、動作后落的順序杜絕了兩次操作之間的競態(tài)。add_message還返回它暫存的 link id一個(gè)排隊(duì)的創(chuàng)建動作還沒有seq要等 owner 應(yīng)用動作時(shí)倉庫才分配。郵箱命名像服務(wù)器那樣命名而不是像倉庫那樣命名變更后郵箱由它的服務(wù)器名字命名。倉庫 collection 的 key 是namespace/name所以imap/INBOX這個(gè) collection 對外就是郵箱INBOX。命名空間的推導(dǎo)規(guī)則是當(dāng)賬戶的所有郵件 collection 共享同一個(gè)前綴時(shí)才推導(dǎo)出命名空間pimdir.namespace可以覆蓋推導(dǎo)結(jié)果如果一個(gè)倉庫的郵件 collection 橫跨兩個(gè)命名空間則保持完整 id 作為名字而不是把兩個(gè)郵箱折疊成一個(gè)。用戶輸入的名字按以下順序解析hub_id在 src/pimdir/backend.rs 中實(shí)現(xiàn)先枚舉該賬戶的郵件 collection只保留message/rfc822種類見is_mail并優(yōu)先把完整 collection id 按原樣接受名字匹配不到任何 collection、或匹配到多個(gè)時(shí)拒絕并列出賬戶實(shí)際持有的候選而不是當(dāng)作空郵箱默默讀取名字絕不未經(jīng)解析就直接傳給倉庫——那正是舊實(shí)現(xiàn)讀到存在但為空的根因。delta 中還給出了兩個(gè)驗(yàn)收場景配置的 inbox 別名解析倉庫 collection 位于imap命名空間下命令帶-m INBOX或不帶郵箱且mailbox.alias.inbox INBOX時(shí)應(yīng)當(dāng)列出imap/INBOX未知郵箱報(bào)出實(shí)情-m Nope時(shí)應(yīng)當(dāng)失敗并列出賬戶持有的郵箱而不是列出空列表。由于 hub id 是用戶輸入 → 賬戶郵件 collection → 解析倉庫里一個(gè)從未被寫入的 collection id 會直接報(bào)Mailbox \{mailbox} not found in the pimdir store, which holds: ...把錯誤暴露在入口而不是讀成空數(shù)據(jù)。讀取單賬戶pimdir.account取代pimdir.source舊配置里的pimdir.source被移除生產(chǎn)者不為任何動作歸屬 source——owner 把隊(duì)列當(dāng)作自己來應(yīng)用。讀者真正需要的答案是它展示的是哪個(gè)賬戶的 collectionpimdir SPEC §9.2因此配置項(xiàng)改為pimdir.account。推導(dǎo)邏輯在 src/pimdir/client.rs 的resolve_account中顯式配置了pimdir.account就直接使用未配置時(shí)枚舉倉庫所有 collection 的 account 歸屬并去重只有一個(gè)賬戶含一個(gè)未分組的集合→ 就讀這個(gè)賬戶倉庫被多個(gè)賬戶共享 →拒絕并列出各賬戶名提示用pimdir.account指定絕不猜測——猜一個(gè)就會顯示錯誤的郵箱集合。對應(yīng)的配置字段在 src/config.rs 的PimdirConfig中root倉庫目錄支持~展開存放pimdir.db與objects/與account可選。config.sample.toml 中給出了完整示例# 一個(gè)你已在同步的賬戶加下面這行就夠了全程不涉網(wǎng)絡(luò) #pimdir.root ~/.local/state/neverest/example # 讀取哪個(gè)同步賬戶的 collection。通常留空即可 # 單賬戶同步的倉庫被讀為該賬戶多賬戶共享時(shí)必填。 #pimdir.account posteo # 郵箱即 collection id 原樣服務(wù)器叫 INBOX 的郵箱在這贊分享CLI【免費(fèi)下載鏈接】himalayaCLI to manage emails項(xiàng)目地址https://gitcode.com/gh_mirrors/hi/himalaya點(diǎn)擊查看免費(fèi)下載相關(guān)推薦Home Assistant 使用 xiaomi_miio.switch_set_wifi_led_off 關(guān)閉米家智能插座/插線板 Wi-Fi 指示燈Home Assistant 使用 xiaomi_miio.switch_set_wifi_led_off 關(guān)閉米家智能插座/插線板 Wi Fi 指示燈 本文以CLIHimalaya pimdir 后端重構(gòu)解析從同步引擎 Store 的所有者到讀者 生產(chǎn)者Himalaya pimdir 后端重構(gòu)解析從同步引擎 Store 的所有者到讀者 生產(chǎn)者 本文深入剖析 HimalayaRust 編寫的命令行CLIHimalaya pimdir 后端合并重構(gòu)解讀io-pimdir 一庫承載存儲與同步引擎Himalaya pimdir 后端合并重構(gòu)解讀io pimdir 一庫承載存儲與同步引擎 本文以 Himalaya 倉庫中 pimdir merged enCLI上一篇FridgeChef 實(shí)戰(zhàn)用 SwiftUI 與 AI 輔助在 easy-vibe 中從零開發(fā)原生 iOS 應(yīng)用下一篇NocoBase 服務(wù)端插件開發(fā)Database 數(shù)據(jù)庫核心機(jī)制與實(shí)戰(zhàn)指南創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考