FAkka.FSI.Supervisor
1.569.101.302
dotnet add package FAkka.FSI.Supervisor --version 1.569.101.302
NuGet\Install-Package FAkka.FSI.Supervisor -Version 1.569.101.302
<PackageReference Include="FAkka.FSI.Supervisor" Version="1.569.101.302" />
<PackageVersion Include="FAkka.FSI.Supervisor" Version="1.569.101.302" />
<PackageReference Include="FAkka.FSI.Supervisor" />
paket add FAkka.FSI.Supervisor --version 1.569.101.302
#r "nuget: FAkka.FSI.Supervisor, 1.569.101.302"
#:package FAkka.FSI.Supervisor@1.569.101.302
#addin nuget:?package=FAkka.FSI.Supervisor&version=1.569.101.302
#tool nuget:?package=FAkka.FSI.Supervisor&version=1.569.101.302
Akka.FSI.Supervisor
Akka.FSI.Supervisor 是一個基於 Akka.NET 的 F# Interactive (FSI) 會話管理器。它提供了在分散式或單一行程的 Akka 系統中,安全且可擴展地執行 F# 腳本 (FsiEvaluationSession) 的機制。
程式架構 (Architecture)
本專案採用 Actor Model 設計模式,主要元件與階層如下:
FsiGuardianActor(/user/fsi)- 負責管理並生成
FsiSupervisorActor,確保系統中有一個固定的存取點。
- 負責管理並生成
FsiSupervisorActor(/user/fsi/supervisor)- 系統的核心路由與調度者。
- 負責接收所有客戶端請求 (如
ExecCode,GetSessionInfo, 虛擬檔案操作),並將其轉發給對應的FsiWorkerActor。 - 管理 Session 的生命週期(自動建立、重置)。
- 整合
PcslFileSystemActor處理分散式/持久化虛擬檔案系統 (基於 PCSL - PersistedConcurrentSortedList)。 - 提供
Fork、Join等高階工作階段操作機制。
FsiWorkerActor(/user/fsi/supervisor/session-{name})- 每一個 FSI Session (例如 "default", "session-A") 對應一個專屬的
FsiWorkerActor。 - 封裝了一組
FsiSession.Handle(底層呼叫FSharp.Compiler.Service的FsiEvaluationSession)。 - 確保同一個 Session 的 F# 程式碼循序執行,避免多執行緒共用狀態的問題。
- 自動擷取標準輸出 (stdout)、標準錯誤 (stderr) 以及最後執行的結果值 (
fsi_it),並將結果封裝成ExecResult。
- 每一個 FSI Session (例如 "default", "session-A") 對應一個專屬的
PcslFileSystem&PcslFileSystemActor- 提供依賴於 PCSL 的虛擬檔案層,可存放
.fsx腳本或結果。
- 提供依賴於 PCSL 的虛擬檔案層,可存放
Journal / Snapshot / DB
Akka.FSI.Supervisor 目前不使用 Akka.Persistence journal 或 snapshot-store,因此不會建立 journal、EventJournal、snapshot 或 SnapshotStore 這類資料表。
本專案有 PCSL 虛擬檔案系統設定,但它是 PCSL keyspace,不是 SQL table:
akka.fsi.pcsl2 {
scripts-keyspace = "scripts"
results-keyspace = "results"
snapshots-keyspace = "snapshots"
auto-commit = true
}
預設值來自 SupervisorSettings.PcslSettings.Default。目前沒有 hardcoded database name;實際 PCSL backing store 由建立 PcslFileSystemActor 時傳入的 storage/hub 決定。
Current backing root selection:
PcslStorage.pcsl2 root "fsi"createsPersistedConcurrentSortedList<string, fCell2<string>>(40, root, "fsi").AkkaFsiBootstrapresolves a writable PCSL root fromAKKA_FSI_PCSL_ROOT,SHARFTRADE_PCSL_ROOT, current working directorypcsl, or a temp fallback underakka-fsi-pcsl.PcslStorage.inMemory()is still file-backed PCSL under temp root; the name means development fallback, not Akka in-memory journal.
PCSL logical keys:
| Keyspace | Key shape | Value |
|---|---|---|
scripts |
scripts/<normalized path> |
script text as fCell2<string> |
results |
results/<session-or-result-id>.json |
serialized ExecResult / session result JSON |
snapshots |
snapshots/<session>/<checkpoint>.json |
checkpoint marker/payload JSON |
There is no SQL schema or CREATE TABLE statement in this package.
核心 API 與介面
對外溝通主要透過 Akka.NET 的訊息傳遞 (Message Passing) 或使用 AkkaFsiBootstrap 提供的 Helper 模組。
AkkaFsiBootstrap (位於 Api.fs)
提供啟動、初始化 Supervisor 與封裝好的非同步函數。
EnsureSupervisor(system: ActorSystem): 在指定的 ActorSystem 啟動 Guardian 與 Supervisor。GetSupervisor(system: ActorSystem): 取得 Supervisor 的IActorRef。ExecJson(supervisor: IActorRef, json: string): 接收 JSON 格式的ExecCode請求,並非同步回傳ExecResult的 JSON 字串。GetSessionInfoJson(supervisor: IActorRef, session: string): 取得特定 Session 的狀態。
主要訊息協定 (Contracts)
(定義於 Akka.FSI.Contracts,序列化採用 JsonCodec)
ExecCode/ExecCodeJson: 提交程式碼執行請求。session: 目標 Session 名稱 (預設為 "default")。code: 要執行的 F# 程式碼字串。refs/loads: 需要載入的參考 dll (#r) 或腳本 (#load)。
ExecResult: 執行完成的結果。ok: 執行是否成功。resultJson: 執行的最後回傳值 (it)。stdout/stderr: 輸出。diagnostics: 編譯/執行階段的錯誤或警告。
EnsureSession/ResetSession/DeleteSession: 管理 session lifecycle。EnsureSession會建立或回傳既有 session。ResetSession保留重置語意,會移除目前 worker actor 與 session cache,使下次 exec 重新建立乾淨 session。DeleteSession是 UI/remote lifecycle 的真正刪除語意,會移除 worker actor、session cache、output history 與 subscribers;不可用 reset-generated session name 模擬 delete。
SubscribeSessionOutput/UnsubscribeSessionOutput: 讓遠端 caller 直接向FsiSupervisorActor註冊/移除 session output subscriber。subscriberId: caller 自行提供的 subscriber key,同一 session 內可覆寫同名 subscriber。fromSequenceNo+includeHistory: 可要求 supervisor 以SessionOutputEvent(isReplay = true)replay 已保存的 session output history。- live output 由
FsiWorkerActor在每次ExecResult產生後回報給FsiSupervisorActor,supervisor 負責遞增sequenceNo、保留 in-memory history,並以SessionOutputEvent(isReplay = false)推送給 subscriber actor。 - event payload 反映
ExecResult.stdout/stderr;FsiSession會用Console.SetOut/Console.SetError的 AsyncLocal router 捕捉 session 執行期間的printfn/eprintfnconsole redirection,避免 stdout 漏到 supervisor process console。
Worker host shared logging profile
版本 1.564.101.203-win5 起,獨立 Akka.FSI.Supervisor worker host 支援 WS-14 shared logging profile seam;目前 1.564.101.203-win6 同步使用 PulseTrade.Infra.Logging [1.564.101.203-win4]:
--logging-hocon <path>:載入 host 產生的 logging HOCON fragment,建議由PulseTrade.Infra.Logging.AkkaHocon.renderNLogLogger產生。PULSETRADE_AKKA_LOGGING_HOCON:未傳--logging-hocon時可由環境變數提供同一個檔案路徑。PULSETRADE_NLOG_CONFIG_FILE:若需 SQL target,host 可用PulseTrade.Infra.Logging.NLogRuntime.writeConfigFile產生 NLog XML config,並透過此 env var 讓 worker process 載入。--logging-profile console:使用 built-in console NLog profile,適合 smoke / diagnostic。--logging-profile none或不指定:保留原本 package 行為,不強制切換 Akka logger。
Package 層不 hardcode SQL Server connection string、NLog table 或正式環境。SQL target / NLog database 應由最終 host 決定,再透過 HOCON/config 注入。
E2E 範例腳本 (FSX)
以下示範如何在一個獨立的 .fsx 腳本中,建立 ActorSystem、掛載 Supervisor,並送出一段 F# 程式碼給 FSI 動態編譯並印出結果。
注意:
- 執行此腳本前,請先
dotnet build Akka.FSI.Supervisor.fsproj -c Debug。protobuf-net-fsharp.dll若不在預設 probing path,請設定環境變數PROTOBUF_FSHARP_DLL指向實際檔案。- README 內的
bin/Debug/net10.0/...路徑只是示意;若你是在 repo 外執行.fsx,請改成實際輸出路徑。
// load_fsi.fsx
#r "nuget: Akka, 1.5.62"
#r "nuget: FSharp.Compiler.Service, 43.12.201"
// 請將 repoRoot 改成你的 PulseTrade.fs 實際路徑
let repoRoot = "/absolute/path/to/PulseTrade.fs"
let outDir = repoRoot + "/Libs/Akka.FSI.Supervisor/bin/Debug/net10.0"
#r outDir + "/FAkka.Fsi.Contracts.dll"
#r outDir + "/Akka.FSI.Supervisor.dll"
#r outDir + "/PersistedConcurrentSortedList.IFileSystem.dll"
open System
open System.Text.Json
open Akka.Actor
open Akka.FSI.Contracts
open Akka.FSI.Supervisor
// 1. 視需要顯式指定 protobuf-net-fsharp.dll 路徑
Environment.SetEnvironmentVariable(
"PROTOBUF_FSHARP_DLL",
outDir + "/protobuf-net-fsharp.dll"
)
// 2. 建立 Akka ActorSystem
let system = ActorSystem.Create("TestFsiSystem")
// 3. 初始化並確保 Supervisor 啟動
let supervisorRef = AkkaFsiBootstrap.EnsureSupervisor(system)
printfn "Supervisor is ready: %A" supervisorRef
// 4. 準備要丟給 FSI 動態執行的程式碼
// 這段程式碼會被送進 FsiWorkerActor 解析
let codeToExecute = """
let add a b = a + b
let result = add 100 50
printfn "Inside FSI: add function evaluated!"
result // 這是 fsi_it, 會被捕捉回傳
"""
// 建立 ExecCode 請求
let execRequest =
{
id = Guid.NewGuid().ToString("N")
session = "session-1" // 指定獨立的 session
code = codeToExecute
refs = []
loads = []
args = None
timeoutMs = Some 10000
captureStdout = Some true
}
// 將 Request 序列化為 JSON (模擬真實 API 呼叫情境)
let jsonRequest = JsonCodec.serialize execRequest
async {
printfn "Sending ExecRequest..."
// 5. 透過 Api.fs 的 Helper 發送請求
let! jsonResponse = AkkaFsiBootstrap.ExecJson supervisorRef jsonRequest
// 6. 反序列化並檢視結果
let result = JsonCodec.deserialize<ExecResult> jsonResponse
printfn "--- Execution Result ---"
printfn "Is OK: %b" result.ok
printfn "Stdout: %s" result.stdout
printfn "Stderr: %s" result.stderr
printfn "Captured It Value (JSON): %A" result.resultJson
// 7. 查詢 Session 狀態
let! sessionInfoJson = AkkaFsiBootstrap.GetSessionInfoJson supervisorRef "session-1"
let sessionInfo = JsonCodec.deserialize<SessionInfo> sessionInfoJson
printfn "Session Status: %s" sessionInfo.status
// 清理資源
system.Terminate() |> ignore
} |> Async.RunSynchronously
system.WhenTerminated.Wait()
printfn "System terminated."
開發與執行規範
- 建置 (Build): 使用
dotnet build Akka.FSI.Supervisor.fsproj。 - 依賴管理: 若需更新套件,請務必使用
dotnet-outdated-tool,嚴禁手動寫 python 腳本替換版本號。 - 環境變數
PROTOBUF_FSHARP_DLL: 在Supervisor.fs(ProtoBufInit模組) 中,系統會嘗試載入protobuf-net-fsharp.dll。這段初始化的目的不是 Akka remoting,也不是GetVesion/ListSessions這類 control-plane 訊息,而是為了本專案同時整合的 PCSL (PersistedConcurrentSortedList) + protobuf 儲存路徑:FsiSupervisorActor啟動時會建立PcslFileSystemActor- 接著會執行
PcslFileSystem.install - PCSL/protobuf runtime model 若沒有先註冊
F# unit,後續儲存/快照/結果相關操作可能在執行期失敗 - 因此
ProtoBufInit.ensureUnitSerializer()是 PCSL/protobuf 初始化需求,不是 Akka remoting 基本需求 - 若 dll 不在預設 probing path,下列環境變數必須指到正確檔案,否則 supervisor 可能能啟動,但 PCSL/protobuf 能力不完整
- Logging 與除錯: Actor 內部均有繼承自 Akka 的
Logging.GetLogger,若 FSI 執行過程中拋出例外,將會包含在ExecResult.diagnostics或error中。
README 範例已驗證的最小前置條件
以下條件下,上述 FSX 範例已在本機驗證可跑:
dotnet build Libs/Akka.FSI.Supervisor/Akka.FSI.Supervisor.fsproj -c DebugPROTOBUF_FSHARP_DLL=<repo>/Libs/Akka.FSI.Supervisor/bin/Debug/net10.0/protobuf-net-fsharp.dll#r指向:.../FAkka.Fsi.Contracts.dll.../Akka.FSI.Supervisor.dll.../PersistedConcurrentSortedList.IFileSystem.dll
若少了 PROTOBUF_FSHARP_DLL 或 PersistedConcurrentSortedList.IFileSystem.dll 指到錯誤路徑,影響通常是:
Akka.Remote本身未必立刻失敗- 但
FsiSupervisorActor啟動中的 PCSL/protobuf 初始化可能失敗 - 進而導致 supervisor actor 無法完整啟動,或後續 checkpoint / storage / filesystem 行為異常
因此在 fsharp-devkit 這類把 FAkka.FSI.Supervisor 當成 remote host 使用的場景中,PROTOBUF_FSHARP_DLL 仍屬部署前置條件,不應省略。
部署診斷注意事項
FAkka.FSI.Supervisor 在 remote host 場景下,常見誤判是:
fsiSupervisorPath能 resolveGetVesiontimeout- 就直接推論成
fsi-supervisor啟動失敗
這個推論不可靠。更穩的診斷順序是:
- 先確認
proc-supervisor本身可 direct ask - 再用 fresh procnode 的
fsiSupervisorPath - 先試:
ListSessionsExecCode
- 最後才用
GetVesion當補充訊號
在 fsharp-devkit 的 deployed remote host 驗證中,曾出現過:
GetVesiontimeout- 但同一個
fsi-supervisor對ExecCode其實正常 - 最終
host isolation與session isolation都成立
因此 GetVesion timeout 只能代表該 control-plane 命令在當下沒有成功回覆,不能單獨作為 FAkka.FSI.Supervisor 整體不可用的證據。
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Akka (>= 1.5.69)
- Akka.Cluster (>= 1.5.69)
- Akka.Logger.NLog (>= 1.5.69)
- Akka.Remote (>= 1.5.69)
- FAkka.Argu (>= 10.1.301)
- FAkka.Fsi.Contracts (= 10.1.301)
- FSharp.Compiler.Service (>= 43.11.302)
- FSharp.Core (>= 10.1.302)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.9)
- PersistedConcurrentSortedList (>= 10.1.301)
- PersistedConcurrentSortedList.IFileSystem (= 10.1.301.2)
- PulseTrade.Comm.Actor.Registry (= 0.1.0-alpha5)
- PulseTrade.Infra.Logging (= 1.569.101.301)
NuGet packages (2)
Showing the top 2 NuGet packages that depend on FAkka.FSI.Supervisor:
| Package | Downloads |
|---|---|
|
FAkka.Proc.Supervisor
Package Description |
|
|
PulseTrade.Shared.fs
Package Description |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.569.101.302 | 112 | 7/31/2026 |
| 1.569.101.302-win5 | 88 | 8/13/2026 |
| 1.569.101.302-win4 | 240 | 8/2/2026 |
| 1.569.101.302-win3 | 94 | 8/2/2026 |
| 1.569.101.302-win2 | 102 | 8/2/2026 |
| 1.569.101.301 | 126 | 6/20/2026 |
| 1.569.101.301-win6 | 96 | 7/19/2026 |
| 1.569.101.301-win5 | 191 | 6/29/2026 |
| 1.569.101.301-win4 | 128 | 6/28/2026 |
| 1.569.101.301-win3 | 110 | 6/27/2026 |
| 1.569.101.301-win2 | 122 | 6/27/2026 |
| 1.564.101.203-win6 | 126 | 5/17/2026 |
| 1.564.101.203-win5 | 167 | 5/3/2026 |
| 1.564.101.203-win4 | 114 | 5/3/2026 |
| 1.564.101.203-win3 | 125 | 5/3/2026 |
| 1.564.101.203-win2 | 110 | 5/3/2026 |
| 1.564.101.203-win1 | 113 | 5/3/2026 |
| 1.564.101.201-win7 | 135 | 4/19/2026 |
| 1.564.101.201-win6 | 125 | 4/19/2026 |
| 1.564.101.201-win5 | 113 | 4/19/2026 |