Blog

Machine cron, file locks, and observable publishing pipelines

Nalyエンジニアリングノート:Machine Cron と flock による決定論的な日次公開

Machine cronは、ロックの厳密運用と決定論的アーティファクトをNalyの公開ジョブへ適用することで、信頼性の基礎要素になる。このノートでは、スケジューリングとファイルロックを単なる調整処理ではなく一次インフラとして扱い、各公開試行を境界付き・検証可能・再実行安全にする。結果は

June 28, 20266 sources

TL;DR2026年6月28日現在、Nalyはmachine cronを公開のガードレールとして扱っており、スケジュールされたスクリプトは flockを使用し、最小化されたbootstrap手順を経て、決定論的なアーティファクトディレクトリへ出力を保存する。 NALY_LOG_ROOTその結果、公開処理は脆弱な自動化から監査可能なパイプラインへ移行し、各実行で明示的なチェックポイントを生成するか、対応可能なトレース付きで失敗し、再試行時には都度の端末出力を推測せず決定論的アーティファクトから再構成できる。

要約

Nalyのワークフローでは、公開の課題は「毎日コマンドを実行する方法」ではなく「公開結果が一度生成され、完全に観測され、再実行可能であることをどのように証明するか」である。実用的な仮説は、ホストcronとファイルレベルのロックが堅牢な制御面になるということだ。ジョブが flockで直列化され、すべての可変副作用が決定論的な実行IDでゲートされ、ログがリポジトリ外に書き込まれるなら、プロセスの再起動や重複トリガが発生しても日次パイプラインは運用上安定する。これにより、公開の正しさが暗黙の前提ではなく証拠で示されるため、獲得とリテンションの両方で長期的信頼構築が可能になる。

Nalyの中での位置づけ

Nalyの公開パスはアプリケーション層(Next.js + Reactレンダリング、Neon向けDrizzle ORM、アーティファクトアップロード)で大部分が構成されるが、cronはそれらのシステムが作業を受け取る前段の実行契約である。この境界は重要で、公開される予測ノートは外部呼び出し、DB書き込み、生成ファイルに依存しており、これらが部分的に成功し得るからだ。

NalyにおけるMachine cronラッパーは、「毎日実行する」という時間意図(time intent)と「レコードXを公開し、ファイルYを生成し、決定論的な証拠Zを示す」という観測可能なアクションとの間の境界にある。

この設計は、獲得・リテンションのスタックで稼働する戦術(例:定例の記事生成ジョブと定例インサイト配信ジョブ)を支える一方、決定論的保証が証明されるまで、すべてのワークフローを大規模オーケストレーターへ移して複雑化を重ねることを避ける。

技術的メカニズム

Nalyのcron安全公開フローは4つの層で構成される。

  1. スケジュール層:crontabは分・時・日・月・週の実行セマンティクスを提供し、毎分実行エントリを評価する。ドキュメントではフィールド一致ルールに加え、タイムゾーンやDSTの境界動作が明示されており、これらは正しさの前提として扱わなければならない。

  2. 排他制御層:各ラッパーは重要区間を排他的にロックするために flock を使い、通常はノンブロッキングで動作するため、2回目の呼び出しが重複ジョブを積み上げる代わりに既知のコードで終了する。

  3. ブートストラップ層:ランタイムは意図的に簡素化され、明示的にする。ラッパーは(本プロジェクトでは .env.local )必要な環境値を読み込み、実行IDを定義し、永続公開ターゲットへの書き込み前にsmokeモードで必須前提条件を検証する。

  4. 観測層:ログとアーティファクトは外部ルート(NALY_LOG_ROOT)内の実行ごとの決定論的なディレクトリに書き込まれる。ディレクトリ名は規格化タイムスタンプまたは実行IDから導出され、後で各試行を検証するのに十分なメタデータを保持する。

推奨実行パターン:

  • Cronが起動する。
  • ラッパーがロック取得を試みる。
  • 成功時はbootstrapとsmokeチェックを実行する。
  • メインの公開コマンドを tsx エントリポイントで実行する。
  • Manifest、出力、構造化ログを固定ロケーションへ出力する。
  • ロックはプロセス終了時に記述子を閉じて解放される。

シェルのサンプル骨子:

#!/usr/bin/env bash
set -euo pipefail

RUN_ID="$(date -u +%Y%m%dT%H%M%SZ)"
LOCK_FILE=${NALY_LOCK:-/var/lock/naly-publish.lock}
ARTIFACT_DIR="${NALY_LOG_ROOT:-/tmp/logs}/publish/$RUN_ID"
SMOKE_MODE=${NALY_SMOKE:-0}

mkdir -p "$ARTIFACT_DIR"
exec 9>"$LOCK_FILE"
flock -n 9 || exit 75

set -a
. "/path/to/repo/.env.local"
set +a

if [ "${SMOKE_MODE}" = "1" ]; then
  echo "smoke ok"
fi

pnpm tsx scripts/publish.ts --run-id "$RUN_ID" --artifact-dir "$ARTIFACT_DIR"

文献レビュー

このスタックの基盤はLinuxのマニュアルページである: crontab(5) スケジュールセマンティクス、環境変数制御、DST境界ケースを含む時間挙動の微妙な差異を定義する。 flock(1) ファイル/ディレクトリ上のロック作成、非ブロッキングセマンティクス、ロック解放動作を定義する。

システム的な視点では、arXivでのストリーム決定論に関する研究は、厳密な exactly-once の前提よりも、配信一貫性と決定論的処理の方が現実的で実用的であることを示しており、これはNalyが場当たり的リトライより決定論的な再実行性を優先する方針と一致する。同様に、arXivの分散AI推論システムにおける観測可能性研究は、時間順序が弱いとトレースと因果関係が崩れることを警告しており、実行タイムスタンプと集中管理されたアーティファクトルートを利便性ではなく正しさの要件として扱う理由を説明する。

再現性重視の再生可能パイプライン研究も同方向を示している。実用的なパイプラインは再実行可能な版管理済みアーティファクトを生成し、失敗を対処可能にし、証拠を持ち運び可能にするべきである。エージェント系システムについては、最近の構造化観測フレームワーク研究が、運用メタデータは障害後の記録作成のための贅沢ではなく、導入品質の一部であると強調している。

総じて、参照元は一貫して次を示している: flockcrontab/flock形式の排他制御と決定論的なアーティファクト化は、低コストで信頼性を実務的に具現化する具体的なプリミティブである。

設計上のトレードオフ

  • ロックの粒度:グローバルロックは理解しやすいが無関係なジョブまで直列化する可能性がある。ワークフロー別ロックはスループットを上げるが、ロック名ガバナンスがより厳密になる。
  • ブロッキング vs 非ブロッキングロック取得:非ブロッキングは明示的な競合シグナル付きでクリーンに終了し、ブロッキングは停止したジョブを隠し、重複実行窓を拡大しうる。
  • ホストcronの単純性 vs 集約型スケジューラ:cronはインフラの複雑性と影響範囲を下げる一方、ガバナンス(状態、リトライ、重複排除)をアプリケーションコード側へ押し込む。
  • 観測深度 vs コスト:冗長な構造化ログはストレージと分析工数を増やすが、障害後の平均トリアージ時間を実質的に短縮する。
  • 決定論的アーティファクトの保持期間 vs ストレージ圧力:保持期間を長くすれば再実行性と監査品質は向上するが、ライフサイクル方針を追加しないと保有期間が長くなりコストが増加する。

障害モード

  • 重複実行:前の実行がまだアクティブでロック解放が遅れる場合に発生する。 flock これは非ブロッキングで緩和され、競合時の明示的終了コードにより対応できる。
  • ロックバックエンドの制約: flock は特定のファイルシステム(NFS/CIFS)で失敗する場合があるため、可能な限りロックパスはローカルディスク上に置くべきである。
  • DST遷移時の欠落または重複呼び出し:cronのセマンティクスによりウィンドウが欠落または重複する場合がある。実行IDベースのべき等性と重複排除チェックで緩和する。
  • 部分失敗から生じる古いプロセスアーティファクト:実行ごとのアトミック書き込みパターンとmanifestチェックポイントで回避する。
  • 非決定論的副作用:デデューンプなしの固定時刻リトライは重複公開を招く。下流の状態ストアでべき等な書き込みとユニーク制約を用いて緩和する。
  • ログにおける不安定な時刻前提:同期されていない時計に起因する観測可能性の失敗はトレースの順序を逆転させる。UTCベースの実行タイムスタンプと安定したシーケンスメタデータで緩和する。

実装メモ

Nalyでは実践的な目標状態は以下のとおりである:

  • machine crontabでcron式を実行し、対話型コンポーネントを持たない。
  • flock 各公開タスクの呼び出しごとにロックする。
  • 必須のsmokeモードと明示的な終了コード。
  • tsc/tsx エントリポイントはラッパーから提供し、暗黙のシェル実行パスに頼らない。
  • 実行ID・日付・ジョブIDを含むアーティファクトディレクトリ構造。
  • 決定論的な名前で NALY_LOG_ROOT 構造化ログを書き込む。
  • 永続境界でべき等性を持つように設計された公開ジョブ。

運用上、ここで「自動化」が「公開インフラ」に変わる。安定したスケジューリング、保護された同時実行制御、検査可能な出力が、信頼できるコンテンツ公開の最小インターフェースになる。

参考文献

Sources