event の設計
設計目標
イベントストリームは実験の監査記録です。レポートのすべての数値とリプレイされるすべての失敗は、そこから復元できなければなりません。event はこの記録を追記専用かつ行指向に保ち、ストレージの選択をランナーから切り離します。
数学的背景
実行をイベントの系列 、シンクを畳み込み としてモデル化します。JSONL シリアライズはイベントから行への写像 であり、保存されるストリームは行 を改行で連結したものです。
この構造から 2 つの性質が導かれます。
- 接頭辞について閉じている。 ストリームのどの接頭辞も、より短い実行のストリームです。 個のイベントの後にクラッシュしても の有効な記録が残り、行を独立に処理するリーダーはそれを利用できます。
- 射影は連結と可換。 レポートは行ごとに構築されるため、連結された 2 つのストリームの射影は、それぞれの行の射影から計算されます。ストリームのマージやフィルタリングは単なるテキスト処理です。
tee は 2 つの畳み込みの積です。 を に写すため、1 回のパスで実行をメモリとディスクの両方に保存できます。
設計上の決定
クロージャのレコード
問題。 シンクは状態(配列、ファイルハンドル)を持ち、組み合わされます(tee)。選択肢。 メソッドを持つトレイト、または関数のレコード。選択。 5 つのクロージャからなるレコード。理由。 クロージャはシンクが必要とする任意の状態を捕捉でき、レコードはテスト内でその場で構築でき、tee のようなコンビネータは新しいレコードを返す通常の関数になります。
1 行に 1 つの JSON オブジェクト
問題。 記録はストリーミング可能で、追記可能で、どんなツールからも読めなければなりません。選択。 すべての行に type 判別子と artifact_version を持つ JSONL。理由。 各行が自己記述的なので、リーダーは知らない種類を読み飛ばせ、grep が使え、クラッシュしてもそれ以前の行が壊れることはありません。各行のバージョンにより、異なるバージョンのストリームを連結しても、なお正確に拒否できます。
生の値のみ
観測は 1 つのバッチの反復あたりの時間を持ち、平均、中央値、フィルタリングされた値を持つことはありません。レポートのすべての統計量はこれらの生の値から再計算され、外れ値ポリシーが履歴を書き換えることはできません。
文字列としてのシード
seed は 10 進文字列として書かれます。JSON の数値は倍精度浮動小数点数であり、すべての 64 ビット整数を表現できないためです。 を超える整数は丸められてしまいます。
finish はロケーションを返す
finish は記録の行き先(memory://run/…、jsonl://memory/…、ファイル名)を返し、ランナーはそれを要約に格納します。ランナーがファイルについて知らなくても、呼び出し側は証拠の所在を知ることができます。
正しさと不変条件
- 各 emit 呼び出しは、ちょうど 1 行を書き出す(
JsonlSink、streaming_jsonl)か、ちょうど 1 つの要素を追加します(InMemorySink)。 - すべての行は
artifact_versionmmka_1とtypeを持ちます。 teeは順序を保ちます。どのイベントも、左のシンクが右のシンクより先に受け取ります。to_jsonlはイベントごとに 1 行を持ち、行は\nで区切られます。- 文字列は JSON エンコーダによってエスケープされるため、行が生の改行を含むことはありません。
採用しなかった代替案
- 実行ごとに 1 つの JSON ドキュメント。 追記できず、クラッシュするとすべてが失われます。
- CSV。 入れ子になった証拠(オペランド、フラグ、環境)が収まりません。
- バイナリ形式。 レビューや issue の報告で読めません。
境界
- ファイル IO は行いません。行をディスクに書き出すのは呼び出し側の役割です(
cliとアプリケーションがこれを行います)。 InMemorySinkは要約を保持しません。- JSONL ライターは、検証ステータスの理由文字列と観測の
setup_timingを省略します。 - リーダーはここにはありません。ストリームを解析するのは
reportとcliです。