疑難排解
大多數問題會先反映在 fork 的狀態上,然後才在其他地方出現。先找到 fork、讀取它的狀態,再依照該狀態、你看到的日誌訊息或症狀,查閱對應的那一列。
找出 fork
Section titled “找出 fork”Graft 記錄外掛失敗時會附上完整的 Cause,並帶有 graft.plugin 和 graft.fork
註解。正在等待的 fork 不會寫入任何日誌:Pending 和長時間的 Loading
只會出現在 fork 樹中。
| 查看位置 | 顯示內容 |
|---|---|
graft start 的輸出 |
失敗及其 Cause,以及下列訊息。外掛的日誌行會標明 fork。 |
Graft.forks |
每個 fork 的 name、id 和 state,包括 Pending 的 missing 與 Failed 的 cause。 |
fork.state |
你自己載入的單一 fork,直到 Disposed 為止的每次變化。 |
要記錄一直停在 Pending
的 fork,請加入用 fork 樹診斷中的外掛。
http-gateway
範例透過 HTTP 伺服器掛載 GET /_graft,顯示即時外掛樹。
| 狀態 | 要檢查什麼 |
|---|---|
長時間處於 Loading |
apply 尚未回傳。在外掛整個生命週期內持續執行的工作,例如 Stream.runForEach、伺服器迴圈或 Effect.never,應放進 Plugin.fork。模組匯入一直沒有完成的設定項目也會停在 Loading。 |
Pending |
請參閱 Pending。 |
Failed |
讀取 cause:在錯誤中找到它的錯誤標籤,修正輸入、相依項目或程式碼。fork 何時再次嘗試,請參閱失敗與重試。 |
Active,但沒有任何動靜 |
請參閱 Active 但沒有動靜。 |
Pending
Section titled “Pending”missing
列出必要服務的鍵字串,這些服務在 fork 的領域和環境中都沒有提供者。依序檢查:
- 提供者載入了嗎? 處於
Failed、Pending或已停用的提供者不會發布任何東西。在樹中找到它。 - 它在同一個領域嗎? 位於隔離該鍵的 fork 之內的提供者,只會在該領域中發布。請參閱領域。
- 鍵相符嗎? 服務依鍵字串(例如
app/Database)完全比對。設定項目在graft.yml中的inject欄位會讓它等待更多鍵。 - 兩個外掛在互相等待嗎? 如果雙方各自注入對方發布的服務,兩者都會停在
Pending。把共用的部分移到第三個外掛,讓兩者都注入它。
Active 但沒有動靜
Section titled “Active 但沒有動靜”| 原因 | 修正方式 |
|---|---|
工作以 Effect.forkChild 啟動。它會在 apply 回傳時被中斷,而中斷不會讓外掛失敗。 |
改用 Plugin.fork 啟動。 |
以 Effect.forkScoped、原生計時器或分離的 promise 啟動的工作悄悄失敗了。 |
Plugin.fork 會回報失敗:外掛失敗,並且可以重試。請參閱外掛中的 Effect。 |
處理函式或資源是在 Effect.scoped 內或在某次事件處理函式呼叫中設定的。 |
它隨著那個 scope 一起結束了。請在 apply 中註冊。請參閱處理函式的 context。 |
| 啟動時缺少某個可選相依項目。 | 外掛在沒有它的情況下執行,等它出現時再重新啟動。如果外掛少了它就無法運作,請改為必要相依。 |
| 任務捕捉每個錯誤後繼續執行。 | 讓你無法處理的失敗使任務失敗。請參閱外掛中的 Effect。 |
與外掛相關的訊息列在
Graft 記錄的內容中。下列來自 graft start
和 loader 的訊息,指出程式碼或設定有問題:
| 訊息 | 含義 |
|---|---|
Restart required… |
某個模組變更無法熱重載,舊的外掛繼續執行。請參閱重載不適用的情況。 |
Reloading plugin modules failed |
新程式碼無法匯入或未通過檢查,舊的外掛繼續執行;修正程式碼後再儲存一次。 |
Kept the previous code: plugin "<name>" … |
取代失敗或逾時;計入的恢復 fork 為活躍或 Pending。失敗帶有完整 cause,逾時沒有 cause。修正程式碼後再儲存一次。 |
Replacement abandoned: plugin "<name>" … |
RecoveryFailed:放棄新程式碼的原因。錯誤帶有完整 cause,逾時會指出外掛。 |
Previous code failed to restart: plugin "<name>" … |
RecoveryFailed:恢復的舊外掛也失敗了。錯誤帶有完整 cause。修正啟動失敗後再儲存一次。 |
Waiting for valid configuration in <file> |
在 --watch 下,第一份 graft.yml 無效。請參閱第一份設定無效。 |
Dropped an edit to "<id>": the file changed it too |
一次 API 編輯輸給了檔案中的編輯。請參閱與外部編輯的衝突。 |
| 症狀 | 原因與修正 |
|---|---|
對服務 yield* 時出現型別錯誤 |
該鍵不在 inject 中,或被列為 Plugin.optional:可選的服務請用 Effect.serviceOption 讀取。請參閱相依。 |
型別錯誤中提到 Tasks |
某個輔助函式標註的型別丟掉了 Plugin.Tasks。請參閱型別中的任務錯誤。 |
defect Plugin.fork requires a plugin activation |
Plugin.fork 在外掛之外執行,例如在宿主程式中。請從外掛中呼叫它;在宿主中,請用 scope 持有 fiber。 |
fork.await 或 loader.ready 已回傳,但外掛沒有在執行 |
兩者都把 Pending 視為已完成,ready 也把 Failed 的 fork 算作已完成。請在 fork.state 上等待 Active。請參閱await、stable 和 state。 |
| 另一個外掛載入或重載時,某個消費者跟著重新啟動 | 它的提供者重新啟動了,而消費者一定會隨之重新啟動;讓提供者原地更新。或者消費者使用了可選相依或環境後備,而它的領域中出現了提供者:請參閱查找順序。 |
| 外掛一再重新啟動 | 每次 retry 嘗試都以相同方式失敗。請讀取記錄下來的 cause;排程無法修正持續存在的錯誤。 |
| 消費者看到的是環境中的值,而不是發布的值 | 提供者在另一個領域中發布。請參閱環境後備與繼承。 |
| 處理函式從未被呼叫 | 派發在另一個領域中執行、被過濾器排除、鍵字串不同,或它的 scope 已經關閉。此外,Event.emit 在第一個失敗處停止,Event.bail 則在第一個 Some 處停止。 |
| 處理函式看到錯誤的服務、設定或日誌註解 | 處理函式使用自己所屬外掛的 context 執行,而不是派發端的 context。請把它需要的東西作為事件引數傳入。請參閱處理函式的 context。 |
編輯 graft.yml 沒有效果 |
graft start 執行時沒有加上 --watch;檔案無效,Graft 保留最後一個有效版本(無效的檔案);或者更新函式回傳 true,卻沒有套用變更的欄位。 |
| 變更環境變數沒有效果 | !env 在設定項目載入、變更或重新整理時讀取,而不是在行程環境變更時。使用 --watch 時,儲存 --env-file 指定的檔案會重新整理它。請參閱何時重新讀取值。 |
| 編輯模組沒有效果 | graft start 執行時沒有加上 --watch;檔案位於設定檔目錄之外或在 node_modules 中;設定項目載入的是建置後的 JavaScript,而它沒有重新建置;或者重載失敗了。請參閱什麼會觸發重載。 |
| 重載之後舊行為仍在繼續 | 某個模組在匯入時啟動了計時器或連線,Graft 無法停止它。把它移進 apply。請參閱外掛模組的規則。 |
設定項目以 ConfigError 失敗 |
某個 !env 變數在行程環境、--env-file 檔案和 --env-dir 目錄中都沒有設定。只有這個設定項目失敗。設定該變數後重新啟動;在 --watch 下也可以儲存 env 檔案(loader.retry 會重新讀取)。請參閱環境變數。 |
設定項目以 ModuleNotFound 失敗 |
它的 name 無法解析;訊息會列出嘗試過的名稱。請參閱外掛名稱。 |
設定項目以 ModuleInvalid 失敗 |
模組無法匯入,或者既沒有以預設匯出、也沒有以 plugin 匯出外掛。 |
graft start 立即結束 |
沒有 --watch 時第一份設定無效,或者找不到 --env-file 指定的檔案或 --env-dir 指定的目錄。請參閱 graft start。 |
套件管理器列出兩個版本的 effect 或 @graftland/graft |
某個外掛套件直接相依於它們。它們只能有一份。請參閱外掛套件。 |
| Remote 外掛忽略程式碼編輯 | 沒有 --watch 時,請重新啟動宿主。有 --watch 時,如果設定項目指向建置後的 JavaScript,請重新建置;網路 runner 的程式碼不受監看。請參閱設定變更與熱重載。 |
停不下來的停止
Section titled “停不下來的停止”提供者要等到其服務的消費者都停止之後才會停止,順序見服務撤回流程。這個過程超過 10 秒時,Graft 會記錄
Still waiting for consumers of <keys>; a teardown cycle may be holding them
並繼續等待;關閉、重新啟動和熱重載時都是如此。
| 要找的情況 | 修正方式 |
|---|---|
| 消費者的 finalizer 在等待某件提供者停止後才會做的事。 | 不要在 finalizer 中等待提供者;釋放資源後就返回。 |
| 經由服務或事件形成的環:A 的清理等待 B,而 B 的清理等待 A。 | 用第三個外掛擁有共用的部分,打破這個環。 |
消費者在 Effect.uninterruptible 中的任務等待某件永遠不會來的事。 |
讓等待保持可中斷,只把關鍵步驟設為不可中斷。 |
停止卡住卻沒有任何警告時,可能是某個模組匯入一直沒有完成;請參閱限制。
在 Cause
中找到錯誤標籤,再到錯誤中列出該標籤的章節,查看該怎麼處理。
| 標籤 | 章節 |
|---|---|
SchemaError、Fork.CleanupError、任務失敗 |
fork 失敗 |
ServiceConflict |
服務 |
Graft.ReplacementConflict |
取代 |
UnregisteredReference、InvalidIntercept |
Intercept |
CyclicConfig、InvalidOperation、StoreError、LoaderClosed |
Loader 與設定檔 |
HmrError、Skipped 結果 |
熱重載 |
RemoteError、Contract.UndeclaredFailure、RemoteWithdrawn、RemoteEntryError |
Remote |