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
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="FAkka.FSI.Supervisor" Version="1.569.101.302" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="FAkka.FSI.Supervisor" Version="1.569.101.302" />
                    
Directory.Packages.props
<PackageReference Include="FAkka.FSI.Supervisor" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add FAkka.FSI.Supervisor --version 1.569.101.302
                    
#r "nuget: FAkka.FSI.Supervisor, 1.569.101.302"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package FAkka.FSI.Supervisor@1.569.101.302
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=FAkka.FSI.Supervisor&version=1.569.101.302
                    
Install as a Cake Addin
#tool nuget:?package=FAkka.FSI.Supervisor&version=1.569.101.302
                    
Install as a Cake Tool

Akka.FSI.Supervisor

Akka.FSI.Supervisor 是一個基於 Akka.NET 的 F# Interactive (FSI) 會話管理器。它提供了在分散式或單一行程的 Akka 系統中,安全且可擴展地執行 F# 腳本 (FsiEvaluationSession) 的機制。

程式架構 (Architecture)

本專案採用 Actor Model 設計模式,主要元件與階層如下:

  1. FsiGuardianActor (/user/fsi)

    • 負責管理並生成 FsiSupervisorActor,確保系統中有一個固定的存取點。
  2. FsiSupervisorActor (/user/fsi/supervisor)

    • 系統的核心路由與調度者。
    • 負責接收所有客戶端請求 (如 ExecCode, GetSessionInfo, 虛擬檔案操作),並將其轉發給對應的 FsiWorkerActor
    • 管理 Session 的生命週期(自動建立、重置)。
    • 整合 PcslFileSystemActor 處理分散式/持久化虛擬檔案系統 (基於 PCSL - PersistedConcurrentSortedList)。
    • 提供 ForkJoin 等高階工作階段操作機制。
  3. FsiWorkerActor (/user/fsi/supervisor/session-{name})

    • 每一個 FSI Session (例如 "default", "session-A") 對應一個專屬的 FsiWorkerActor
    • 封裝了一組 FsiSession.Handle (底層呼叫 FSharp.Compiler.ServiceFsiEvaluationSession)。
    • 確保同一個 Session 的 F# 程式碼循序執行,避免多執行緒共用狀態的問題。
    • 自動擷取標準輸出 (stdout)、標準錯誤 (stderr) 以及最後執行的結果值 (fsi_it),並將結果封裝成 ExecResult
  4. PcslFileSystem & PcslFileSystemActor

    • 提供依賴於 PCSL 的虛擬檔案層,可存放 .fsx 腳本或結果。

Journal / Snapshot / DB

Akka.FSI.Supervisor 目前不使用 Akka.Persistence journal 或 snapshot-store,因此不會建立 journalEventJournalsnapshotSnapshotStore 這類資料表。

本專案有 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" creates PersistedConcurrentSortedList<string, fCell2<string>>(40, root, "fsi").
  • AkkaFsiBootstrap resolves a writable PCSL root from AKKA_FSI_PCSL_ROOT, SHARFTRADE_PCSL_ROOT, current working directory pcsl, or a temp fallback under akka-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/stderrFsiSession 會用 Console.SetOut / Console.SetError 的 AsyncLocal router 捕捉 session 執行期間的 printfn / eprintfn console 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 動態編譯並印出結果。

注意:

  1. 執行此腳本前,請先 dotnet build Akka.FSI.Supervisor.fsproj -c Debug
  2. protobuf-net-fsharp.dll 若不在預設 probing path,請設定環境變數 PROTOBUF_FSHARP_DLL 指向實際檔案。
  3. 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.diagnosticserror 中。

README 範例已驗證的最小前置條件

以下條件下,上述 FSX 範例已在本機驗證可跑:

  • dotnet build Libs/Akka.FSI.Supervisor/Akka.FSI.Supervisor.fsproj -c Debug
  • PROTOBUF_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_DLLPersistedConcurrentSortedList.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 能 resolve
  • GetVesion timeout
  • 就直接推論成 fsi-supervisor 啟動失敗

這個推論不可靠。更穩的診斷順序是:

  1. 先確認 proc-supervisor 本身可 direct ask
  2. 再用 fresh procnode 的 fsiSupervisorPath
  3. 先試:
    • ListSessions
    • ExecCode
  4. 最後才用 GetVesion 當補充訊號

fsharp-devkit 的 deployed remote host 驗證中,曾出現過:

  • GetVesion timeout
  • 但同一個 fsi-supervisorExecCode 其實正常
  • 最終 host isolationsession isolation 都成立

因此 GetVesion timeout 只能代表該 control-plane 命令在當下沒有成功回覆,不能單獨作為 FAkka.FSI.Supervisor 整體不可用的證據。

Product 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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
Loading failed