機(jī)制在寫(xiě) closed_at:freehire 永不硬刪除職位的軟關(guān)閉生命周期設(shè)計(jì))
【免費(fèi)下載鏈接】freehirefreehire — the open-source search engine for job seekers項(xiàng)目地址https://gitcode.com/GitHub_Trending/fr/freehire點(diǎn)擊查看免費(fèi)下載freehire 是一款開(kāi)源的職位搜索引擎the open-source search engine for job seekers它每天索引數(shù)十萬(wàn)條職位卻有一條鐵律從不硬刪除任何職位。一個(gè)職位是否活著只由jobs表里的closed_at一列決定——closed_at IS NULL即視為開(kāi)放。寫(xiě)入這個(gè)字段的不是一個(gè)人而是六個(gè)各管一段的自動(dòng)化機(jī)制像接力一樣覆蓋職位下線的所有場(chǎng)景。為什么選擇軟關(guān)閉而不是刪除對(duì)用戶來(lái)說(shuō)刪除是最干凈的做法但對(duì)搜索引擎來(lái)說(shuō)刪除是危險(xiǎn)動(dòng)作鏈接不能斷職位詳情被外鏈、被收藏、被郵件引用過(guò)。freehire 的規(guī)則是——列表、搜索、公司頁(yè)都過(guò)濾closed_at IS NULL但詳情頁(yè)仍然服務(wù)已關(guān)閉的職位歷史鏈接永遠(yuǎn)不 404。可以免費(fèi)重開(kāi)爬蟲(chóng)是有罪推定偶爾會(huì)誤判。職位一旦被錯(cuò)誤關(guān)閉下一輪爬取把它再次看到時(shí)upsert 會(huì)自動(dòng)清空closed_at將其重開(kāi)見(jiàn) TouchJob 中的closed_at NULL邏輯。刪除的行則永遠(yuǎn)找不回來(lái)。數(shù)據(jù)可以審計(jì)freehire 曾在早期有一個(gè)cmd/prune命令批量硬刪不屬于 IT 職位板的條目后來(lái)目錄擴(kuò)展到 IT 之外就退役了相關(guān)記錄歸檔進(jìn)獨(dú)立的pruned_jobs表。項(xiàng)目甚至明確拒絕讓closed_at兼職表達(dá)被清理的含義——遷移 0071_jobs_closed_reason.sql 的注釋寫(xiě)道復(fù)用一列表達(dá)兩種含義會(huì)腐蝕三個(gè)機(jī)制已經(jīng)在寫(xiě)的信號(hào)。一句話總結(jié)關(guān)閉是狀態(tài)刪除是事故。六個(gè)機(jī)制逐一拆解這六條路徑互不重疊五個(gè)基于證據(jù)關(guān)閉一個(gè)基于猜測(cè)關(guān)閉前四個(gè)盯的是單個(gè)職位后兩個(gè)盯的是整塊看板的長(zhǎng)期病史。完整約定見(jiàn) docs/agents/job-lifecycle.md。機(jī)制一ingest 清掃——48 小時(shí)沒(méi)再見(jiàn)就關(guān)unseen這是主力機(jī)制跑在每次爬取之后。爬蟲(chóng)每次成功看到一個(gè)職位都會(huì)刷新它的last_seen_at時(shí)間戳清掃任務(wù)CloseUnseenJobs位于 cmd/ingest/隨后把該來(lái)源下超過(guò) 48 小時(shí)沒(méi)再出現(xiàn)的職位標(biāo)記關(guān)閉。它故意保守只關(guān)本次運(yùn)行真正爬到的公司的職位寧可漏關(guān)也不誤關(guān)。若單條UPDATE因壞行失敗還有逐行兜底CloseUnseenJobByID把整批繼續(xù)走完。機(jī)制二liveness 探針——連續(xù)兩次過(guò)期才判死probe_expired有些職位手動(dòng)導(dǎo)入、URL 解析進(jìn)來(lái)的不在任何爬取范圍內(nèi)機(jī)制一永遠(yuǎn)夠不到它們。cmd/liveness工作器會(huì)直接抓取這些孤兒職位的 URL用純啟發(fā)式規(guī)則HTTP 404/410、跳轉(zhuǎn)到列表頁(yè)、多語(yǔ)言已關(guān)閉措辭等判斷頁(yè)面狀態(tài)連續(xù)兩次讀到expired才關(guān)閉liveness_strikes計(jì)數(shù)器一次健康探測(cè)即清零。它只憑正面證據(jù)行動(dòng)且從不主動(dòng)重開(kāi)——傾向于少關(guān)。機(jī)制三流式源的自我關(guān)閉feed_removed像jobtech瑞典公共就業(yè)局這類(lèi)提供增量變更流的來(lái)源會(huì)在訂閱流里直接推送該職位已下架事件。ingest 管道收到Removed: true后調(diào)用CloseJobBySourceExternalID按(source, external_id)精確關(guān)閉。這類(lèi)來(lái)源因此被排除在機(jī)制一之外——它們的流只報(bào)告變化的職位靠last_seen_at反而會(huì)把仍在線的職位誤關(guān)。機(jī)制四年齡規(guī)則——唯一的猜測(cè)式關(guān)閉expired這是六個(gè)機(jī)制里唯一沒(méi)有證據(jù)可查的telegram來(lái)源的職位 URL其實(shí)就是那條帖子本身招聘結(jié)束后帖子依然在線任何探測(cè)都讀不出已下線。于是 freehire 采用 45 天年齡規(guī)則超過(guò) 45 天直接按expired關(guān)閉CloseStaleUnsignalledJobs跑在 cmd/liveness/ 中。正因?yàn)樗遣聹y(cè)項(xiàng)目刻意把來(lái)源列表交給調(diào)用方顯式傳入——新數(shù)據(jù)源不可能不知不覺(jué)滑進(jìn)年齡關(guān)閉。也正因如此它被單獨(dú)打標(biāo)讓誤關(guān)的 46 天仍在招人的職位可以作為一個(gè)集合被找回。機(jī)制五僵尸看板兜底board_unreachable機(jī)制一/三都很保守但有一個(gè)沒(méi)有上限的盲區(qū)一塊看板如果徹底死了上游退役、封禁我們、遷移域名它的職位會(huì)永遠(yuǎn)開(kāi)放著卻沒(méi)人能證實(shí)它們還存在。cmd/close-chronic-boardscmd/close-chronic-boards/讀取board_health表——每次爬取的成功/失敗史都記錄在這里——對(duì)連續(xù) 60 天CHRONIC_BOARD_CLOSE_WINDOW_DAYS無(wú)法觸達(dá)的看板整體關(guān)閉closed_reason board_unreachable見(jiàn) 0147_jobs_closed_reason_board_unreachable.sql。執(zhí)行前還會(huì)在 SQL 語(yǔ)句內(nèi)部二次校驗(yàn)看板健康度防止讀取到關(guān)閉之間的時(shí)間窗口里看板恰好復(fù)活。機(jī)制六空 Feed 兜底feed_empty這是機(jī)制五的鏡像盲區(qū)看板能訪問(wèn)、每次都返回 HTTP 200但里面一條職位都沒(méi)有。真實(shí)事故中曾有 107 條職位對(duì)著一個(gè)已清空的 Feed 開(kāi)放了一個(gè)月。board_health.last_yield_at記錄看板最后一次真正產(chǎn)出職位的時(shí)間。同一個(gè)工作器的第二遍掃描對(duì)可達(dá)但 30 天EMPTY_FEED_CLOSE_WINDOW_DAYS零產(chǎn)出的看板整體關(guān)閉打上feed_empty標(biāo)簽0159_jobs_closed_reason_feed_empty.sql。 除以上六條自動(dòng)化路徑外還有兩類(lèi)人工關(guān)閉審核員處理舉報(bào)時(shí)的moderated以及雇主在自助面板下架自己發(fā)布職位的employer_closed0175_jobs_closed_reason_employer_closed.sql——不同角色、不同信任級(jí)別用不同標(biāo)簽區(qū)分。closed_reason給每次關(guān)閉蓋一個(gè)責(zé)任章2026 年的遷移 0071_jobs_closed_reason.sql 為jobs表增加了closed_reason列用數(shù)據(jù)庫(kù) CHECK 約束限定其取值并且每次關(guān)閉必須寫(xiě)明是哪個(gè)機(jī)制寫(xiě)的標(biāo)簽寫(xiě)入者含義unseeningest 清掃48h 沒(méi)再?gòu)目窗迳峡吹絧robe_expiredliveness 探針連續(xù)兩次頁(yè)面探測(cè)過(guò)期feed_removed流式來(lái)源變更流報(bào)告職位下架expired年齡規(guī)則超過(guò) 45 天無(wú)證據(jù)可查board_unreachable僵尸看板兜底看板 60 天無(wú)法觸達(dá)feed_empty空 Feed 兜底看板 30 天可達(dá)但零產(chǎn)出moderated/employer_closed審核員 / 雇主人工關(guān)閉這一列的價(jià)值在注釋里說(shuō)得很直白一條規(guī)則如果證明過(guò)于寬泛現(xiàn)在可以單獨(dú)被審計(jì)、單獨(dú)被撤銷(xiāo)。存量行統(tǒng)一保留未知——事后猜測(cè)給歷史數(shù)據(jù)貼標(biāo)簽等于發(fā)明歷史。重開(kāi)時(shí)標(biāo)簽會(huì)被清空有集成測(cè)試 job_close_reason_integration_test.go 守著這條契約避免一個(gè)復(fù)活的職位還掛著上次的死亡證明。值得留意的是遷移 0071_jobs_closed_reason.sql 特意用了no-transactionNOT VALID拆分約束的做法——生產(chǎn)庫(kù)jobs表約 660 萬(wàn)行普通加約束會(huì)長(zhǎng)時(shí)間持有排他鎖排隊(duì)在后面的每個(gè)讀者都會(huì)一起卡死。關(guān)閉之后搜索摘除與免費(fèi)重開(kāi)職位關(guān)閉后要立刻從搜索結(jié)果里消失否則會(huì)出現(xiàn)搜到已下線職位的尷尬。freehire 的答案是search_delete_outbox刪除隊(duì)列0113_search_delete_outbox.sql原子性每條關(guān)閉 SQL 都用 CTE 把UPDATE ... RETURNING id的結(jié)果直接灌進(jìn)刪除隊(duì)列——關(guān)了就入隊(duì)回滾了就什么都不入隊(duì)。無(wú)外鍵刪除隊(duì)列刻意不建指向jobs的外鍵否則職位關(guān)閉 → 入隊(duì) → 行被清理 → 隊(duì)列入被級(jí)聯(lián)刪掉會(huì)讓臟文檔永久滯留在索引里。生產(chǎn)環(huán)境實(shí)測(cè)每天約 19,827 條職位關(guān)閉若不等全量重建這些職位會(huì)在索引里多活 3 小時(shí)。而重開(kāi)完全是免費(fèi)的職位重新出現(xiàn)在爬取結(jié)果中時(shí)upsert 的ON CONFLICT分支清空closed_at順便重置 liveness 失敗計(jì)數(shù)下一輪搜索重建時(shí)它自然回到結(jié)果里——無(wú)需任何人工介入。小結(jié)freehire 的軟關(guān)閉設(shè)計(jì)可以濃縮成三句話一列定生死closed_at IS NULL即開(kāi)放六個(gè)機(jī)制清掃、探針、流事件、年齡規(guī)則、雙兜底分段協(xié)作任何一條路徑誤判都能被下一次的再出現(xiàn)自動(dòng)糾正死亡必須有名字closed_reason讓為什么關(guān)永遠(yuǎn)可審計(jì)、可單獨(dú)回滾連猜測(cè)式關(guān)閉都被明確打標(biāo)永不硬刪關(guān)閉的職位保留public_slug、富化數(shù)據(jù)與用戶收藏引用鏈接永不斷裂重開(kāi)只是清一個(gè)字段。這套用狀態(tài)代替刪除、用標(biāo)簽代替遺忘的生命周期設(shè)計(jì)值得任何管理海量時(shí)效性數(shù)據(jù)的開(kāi)源項(xiàng)目參考。贊分享【免費(fèi)下載鏈接】freehirefreehire — the open-source search engine for job seekers項(xiàng)目地址https://gitcode.com/GitHub_Trending/fr/freehire點(diǎn)擊查看免費(fèi)下載相關(guān)推薦終極指南如何用Fay Agent打造你的24小時(shí)智能管家終極指南如何用Fay Agent打造你的24小時(shí)智能管家 想象一下每天早上醒來(lái)你的智能管家已經(jīng)為你安排好了一天的日程當(dāng)你工作忙碌時(shí)它主動(dòng)提醒你重要的會(huì)RDP Wrapper 完整指南3 步免費(fèi)解鎖 Windows 多用戶遠(yuǎn)程桌面RDP Wrapper 完整指南3 步免費(fèi)解鎖 Windows 多用戶遠(yuǎn)程桌面 周五晚上家里的 Windows 10 是家人追劇用的。你想遠(yuǎn)程連回去取一份文Midway 生命周期Life Cycle完全指南從應(yīng)用啟動(dòng)到優(yōu)雅關(guān)閉的六個(gè)階段Midway 生命周期Life Cycle完全指南從應(yīng)用啟動(dòng)到優(yōu)雅關(guān)閉的六個(gè)階段 生命周期Life Cycle是 Midway 框架中把初始化與后端微服務(wù)云原生上一篇從語(yǔ)言模型卷到 K 線模型金融大模型賽道現(xiàn)在有多擁擠下一篇如何為Android項(xiàng)目配置GitHub Actions CI流水線Parchment的Lint、構(gòu)建與測(cè)試自動(dòng)化實(shí)戰(zhàn)創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考