改了設定要不要重啟?關鍵在「每次請求即時讀」還是「啟動時快取」

現在 code 都讓 AI 寫了,這種判斷它靠得住嗎? 你在資料庫改了一筆設定——權限、feature flag、某個閾值——接著要決定:改完到底要不要重啟服務才生效? 放在以前,你會憑經驗猜:保守派一律重啟(常常多此一舉、白白製造一次中斷),樂觀派賭它即時生效(結果改半天前端還吃舊行為,debug 一小時才發現要重啟)。但現在多了一個選項,也是大多數人實際在做的——直接問 AI。於是真正的問題變成:這種「要不要重啟」的判斷,AI 答得對嗎? AI 會怎麼判斷——而且為什麼常常自信地答錯 先說結論:這題剛好是 AI 最容易出錯的類型,而且會錯得很有自信。 AI 寫 code 的本質,是從訓練資料的 pattern 推測最可能的答案。而「改設定要重啟」在無數系統裡都成立,於是它傾向很篤定地叫你重新部署,卻沒去確認你眼前這份程式碼到底是 live 讀還是快取。版本差異更是重災區——同一個框架舊版快取、新版改成 live 讀,AI 常常混為一談,給你一個「看起來很對」的錯答案。 問題的本質其實沒變,只是換了位置:從前要自己判斷,現在要判斷「AI 的判斷對不對」。而要驗證它、或反過來把它導對,你得先有一把尺——一條不靠經驗、可以拿原始碼直接對答案的準則。 那把尺:即時讀,還是啟動時快取? 決定「改完要不要重啟」的,從來不是設定存在哪裡,而是程式什麼時候去讀它: 讀取時機 改完是否即時生效 典型例子 每個請求即時讀(per-request live read) ✅ 即時,不必重啟 每次查 DB 的權限、即時拉的 feature flag 啟動時載入記憶體後快取(boot / in-memory cache) ❌ 必須重啟或等過期 bootstrap 載入的設定、process 內的計數器 換句話說:改 DB 要不要重啟,等於問「這個值是 live 讀還是被快取了」。把這句話記住,後面所有案例都是它的推論。 麻煩的是,框架很少在文件裡明講某個值屬於哪一類,AI 也答不準。所以正確的態度不是查文件、更不是憑經驗猜,而是翻原始碼,看那個值是在「請求路徑」上被讀,還是在「啟動路徑」上被讀。下面用一正一反兩個真實案例,把這把尺磨利。 實例一:Strapi 權限「改 DB 即刻生效」 需求很單純:把兩個 content-type(就叫它 article 與 category)的 CUD(Create / Update / Delete,建立/更新/刪除)權限,授給幾個自訂角色。Strapi(一個 Node.js 的 headless CMS)的權限存在資料庫的 up_permissions 表,所以最直接的做法就是下 SQL 把對應的 grant 寫進去。 ...

June 19, 2026 · 3 分鐘 · Peter

varchar(255) 陷阱:帳密正確,卻從 in-app 瀏覽器登入回 500

前言:帳密明明正確,卻回 500 某天收到回報:使用者從手機點開官網登入,帳號密碼都對,卻跳出 Internal Server Error。換其他瀏覽器再登入就成功了。 「帳密正確卻 500」這種錯誤特別惱人,因為它不是身分驗證的問題,而是驗證通過之後才出事。本文記錄怎麼從一行日誌追到根因,以及背後三個值得每個後端工程師記住的設計教訓。 從日誌追根因:三條線索 先分清楚登入失敗的兩種回應:帳密錯誤會回 400 Invalid identifier or password;而這次是 500。光這一點就暗示——帳密其實是對的,問題出在登入流程的後半段。 線索一:500 只在帳密正確時出現 撈出該帳號當天的登入紀錄,發現 500 全部集中在某個時段,之後突然出現一次成功,再之後才是幾筆「帳密錯誤」的 400。換句話說,那串 500 本身就證明了帳密一直是對的——錯的不是驗證。 線索二:那行 PostgreSQL 錯誤訊息 日誌裡每筆 500 都附帶同一句資料庫錯誤: insert into "refresh_tokens" (..., "user_agent") values (...) - value too long for type character varying(255) 真相浮現:登入驗證通過後,後端要寫一筆 refresh token 進資料庫,其中 user_agent 欄位是 varchar(255),而這次要寫入的值超過 255 字元,PostgreSQL 直接拒絕,未被攔截的例外往上冒成了 500。 線索三:為什麼有時成功、有時失敗 成功那次寫進資料庫的 user_agent 長度是 128;失敗那幾次的值都超過 255。差別就在 User-Agent 字串的長度——而它由「使用者用什麼瀏覽器」決定。 根本原因:varchar(255) 塞不下 in-app 瀏覽器的 UA 寫入 refresh_tokens 的欄位中,token 是固定長度的隨機字串、expires_at 是時間、device_type 是短列舉、ip_address 也很短。唯一長度會劇烈變動、且可能爆掉的,只有 user_agent。 ...

May 27, 2026 · 2 分鐘 · Peter

Strapi 忘記密碼的安靜回應:Anti-Enumeration、Phishing-as-a-Service 與撞庫經濟學

問題現場:一個「成功」卻收不到信的忘記密碼 某個週一下午,QA 回報:在一個 Flutter + Strapi 架構的 App 上,用測試帳號 [email protected] 點「忘記密碼」,畫面跳出 Success: Reset password link sent successfully on your email account.,但信箱(包含垃圾郵件匣)一封信都沒有。 直覺先排除幾個明顯可能:SMTP 設定壞掉、用戶被 block、信件被 Gmail 擋。但真正的答案比這些都有趣 — 這不是 bug,是 Strapi 一個刻意的安全設計,只是 App 前端把它翻譯錯了。 kubectl 診斷:20ms 與 800ms 的兩種人生 進 Strapi pod 撈 log,找 forgot-password 請求: kubectl logs -n default strapi-prod-xxxxxxxxx-xxxxx --since=6h \ | grep -E 'forgot-password|Reset password URL' 抽出的關鍵幾行: 04:04:52 info: Reset password URL generated: https://example.com/... 04:04:53 http: POST /api/auth/forgot-password (948 ms) 200 05:15:44 http: POST /api/auth/forgot-password (20 ms) 200 05:16:53 info: Reset password URL generated: https://example.com/... 05:16:54 http: POST /api/auth/forgot-password (664 ms) 200 05:17:30 http: POST /api/auth/forgot-password (26 ms) 200 一眼看出兩種節奏:800ms 級別的請求伴隨「Reset password URL generated」log,而 20-30ms 的請求則完全沒有。同一支 API,為什麼耗時差 30 倍? ...

April 21, 2026 · 4 分鐘 · Peter

升級 Strapi v5.31+ 後 JWT 過期就被登出?問題藏在你沒注意到的內建路由裡

症狀:App 開啟 15 分鐘後被強制登出 使用者回報一個詭異的問題:App 正常使用一段時間後,突然被強制登出。重新登入後又好了,但過一陣子又被踢出去。 時間規律很明確——正好 15 分鐘。這恰好是我們 JWT 的過期時間。 直覺反應是去查前端的 token refresh 邏輯:interceptor 有沒有正確攔截 401?refresh token 有沒有正確儲存?重試機制有沒有 bug? 查了一輪,全部正常。 錯誤的除錯方向 這個問題前前後後花了不少時間,方向一直在前端打轉: 排查方向 結果 Flutter interceptor 邏輯 正常,有攔截 401 並觸發 refresh Token 儲存機制(SecureStorage) 正常,refresh token 有正確保存 JWT 過期時間設定 確認是 15 分鐘,符合預期 網路連線問題 排除,其他 API 都正常 每個環節看起來都沒問題,但結果就是 refresh 失敗。問題出在哪? 轉折點:看 HTTP Status Code 直到有人(或者說,終於有人)去看了 /auth/refresh 實際回傳的 HTTP status code: # JWT 過期後呼叫 refresh curl -X POST https://api.example.com/api/auth/refresh \ -H "Content-Type: application/json" \ -d '{"refreshToken": "valid-refresh-token-here"}' # 預期:200 OK(新的 JWT) # 實際:403 Forbidden 403,不是 401。 ...

February 6, 2026 · 2 分鐘 · Peter

會員資料不見?這不是我沒要求,是外包商沒做完整的 OAuth 2.0

故事的開始:會員資料集體消失事件 最近收到許多會員的反映,問題驚人地一致: 📝 「我的日記資料不見了!」 👤 「帳號被自動登出,要重新登入」 ⏳「App 一直轉圈圈,什麼都看不到」 😰 「重新開啟 App 也一樣,資料都不見了」 更詭異的是,這些問題似乎沒有規律性,有的使用者正常,有的使用者卻深受其擾。身為技術負責人,這讓我立刻警覺:這不是資料遺失,而是認證機制出問題了。 調查過程:抽絲剝繭找出真相 問題時間軸 gantt title 問題調查時程 dateFormat HH:mm section 發現問題 接獲使用者回報 :a1, 00:00, 30m section 初步調查 檢查資料庫完整性 :a2, 00:30, 20m 分析回報時間規律 :a3, 00:50, 30m section 深入分析 檢查 JWT 配置 :a4, 01:20, 15m 確認根本原因 :a5, 01:35, 10m 第一步:確認資料庫完整性 我的第一個懷疑是資料庫是否真的遺失資料。登入後台查詢,發現: ✅ 關鍵發現: 所有會員的日記資料都完好無缺 這代表問題不在資料層,而是在存取權限上。 第二步:檢查使用者行為模式 整理會員回報的時間點,我發現一個規律: 會員 註冊時間 問題發生時間 間隔 會員 A 30 天前 今天 30 天 會員 B 29 天前 今天 29 天 會員 C 3 天前 正常使用 - 關鍵字:30 天 ⚡ ...

December 6, 2025 · 9 分鐘 · Peter