はじめに
Snowflake CLI を複数ジョブから並列実行したら、片方だけが不可解なエラーで落ちる——そんな現象に遭遇した方向けの記事です。原因はログローテーションの競合とファイルロックの2つ。この記事では、その切り分け方と対策3つを、接続設定の基本とあわせて示します。
私はデータ基盤の運用保守に携わっており、主にバッチ処理まわりの保守や追加開発を担っています。保守作業の中で、運用中のバッチ処理に Snowflake CLI(snow コマンド) を適用中です。結合試験時に、複数のジョブから同時に呼び出す処理で、片方のジョブだけが落ちる現象に遭遇しました。
調べていくと、並列実行では以下2つのエラーが発生していました。
- 落とし穴①:CLI のログローテート処理でのファイル競合
- 落とし穴②:ファイルロックを解放できないことによるエラー
この記事では、私が実際にハマったこの2つの落とし穴と、その対策(ログファイルの分割とリトライの併用)を、接続設定の基本とあわせてまとめます。
📚 本記事は Snowflake CLI 運用シリーズの第1回です。続編として、
snow connection testが落ちたときにネットワーク層を切り分ける「SnowCD で「Snowflakeに繋がらない」を切り分ける」、本記事の対策③(リトライ)を冪等でないINSERTロードへ安全にかける設計を掘り下げた「リトライを“全エラー一律”にするな」があります。
接続設定の構築
Snowflake CLI は、設定ディレクトリ ~/.snowflake/ 配下の config.toml に接続情報を保存します(このディレクトリが無い場合は OS 既定の場所が使われます)。snow connection add を実行すると、対話的に入力した内容がこのファイルへ書き込まれます。設定ファイルの探索場所の詳細は公式ドキュメント Configuring Snowflake CLI を参照してください。
💡 複数の Snowflake ツール(VS Code 拡張など)で接続を共有したい場合は、接続定義だけを
connections.tomlに分けて置く方法もあります。両方ある場合、CLI はconnections.tomlを優先します。詳細は Managing Snowflake connections を参照。
認証方式は複数ある
Snowflake CLI は Key-Pair(SNOWFLAKE_JWT)、OAuth、外部ブラウザ、MFA、プログラマティックアクセストークン(PAT)、ワークロードID連携など、複数の認証方式に対応しています(一覧は公式の Additional ways to authenticate を参照)。ここでは、今回私が使った PAT(プログラマティックアクセストークン) を例に設定を示します。
PAT は人手を介さずに認証できるトークンで、自動化ジョブに向いています。authenticator を PROGRAMMATIC_ACCESS_TOKEN に設定し、トークンを保存したファイルのパスを token_file_path に指定します。
default_connection_name = "my_connection"
[connections.my_connection]
account = "myaccount.ap-northeast-1"
user = "my_user"
authenticator = "PROGRAMMATIC_ACCESS_TOKEN"
token_file_path = "~/.snowflake/pat_token.txt"
database = "MY_DB"
warehouse = "MY_WH"
role = "MY_ROLE"
PAT の発行手順や有効期限などの仕様は、公式ドキュメント Using programmatic access tokens for authentication にまとまっています。
接続確認
設定後は snow connection test --connection my_connection で疎通確認ができます。正常であれば Status: OK とアカウント情報が返ってきます。CI/CD に組み込む際は、コマンドの終了コードを確認するのが確実です。
💡
snow connection testがエラーになり、それが「認証の問題か、ネットワークの問題か」を切り分けたいときは、公式の接続診断ツール SnowCD が便利です。詳しくは続編「SnowCD で「Snowflakeに繋がらない」を切り分ける」にまとめました。
⚠️ 落とし穴① 日次初回のログローテーション競合
ここからが本題です。並列実行で私が最初にハマったのがこの問題でした。
Snowflake CLI は起動時に ~/.snowflake/logs/ 配下に日次ログファイルを生成します。そして日付をまたいだ初回起動時に、前日分のログをアーカイブするローテーション処理が走ります。
~/.snowflake/logs/
├── snowflake_cli.log # 当日のログ
└── snowflake_cli.log.2025-07-01 # ローテート済みアーカイブ
このアーカイブ処理はファイルへの排他的な書き込みを伴うため、複数プロセスが同時に初回起動すると競合が発生し、片方が例外を吐いてエラー終了します。私の環境では、日次バッチで複数ジョブの初回起動が重なったタイミングで再現しました。
⚠️ 落とし穴② ファイルロックを解放できないエラー
ログローテーションの競合とは別に、並列処理ではもう1つ、ファイルロックを解放できないことに起因するエラー にも遭遇しました。
複数プロセスが同じファイル(CLIの実行環境内部のファイル)へ同時にアクセスする際、一方がロックを保持したまま、もう一方がロックの解放を待ちきれずに失敗します。これは日次初回に限らず、並列度が高いほど起こりやすくなります。
厄介なのは、このエラーがタイミング依存で再現性が低いことです。起動タイミングをずらしても完全には防げません。私の結論としては、確実な回避策はリトライでした(次節の対策③)。
対策① ログファイルの分割(主に落とし穴①に有効)
落とし穴①のローテーション競合に対する根本的な対策は、ジョブごとにログファイルの出力先(パス)を分けることです。共有のログファイルがなくなれば、ローテーション処理が複数プロセスで衝突することもありません。
Snowflake CLI のログ出力先は、config.toml の [cli.logs] セクションの path で指定できます。
[cli.logs]
save_logs = true
level = "info"
path = "~/.snowflake/logs/job_a" # ジョブごとに別ディレクトリにする
ジョブごとに別の設定を読ませる手軽な方法は、設定ディレクトリを指す SNOWFLAKE_HOME 環境変数をジョブ単位で切り替えることです。こうすると各ジョブが自分専用の config.toml とログディレクトリを持つため、ログファイルを奪い合う構造そのものを解消できます。ログ設定の詳細は公式の Configuring Snowflake CLI を参照してください。
対策② 起動タイミングの分散(主に落とし穴①に有効)
別の対策は、各ジョブの起動タイミングを数秒ずらすことです。先頭のジョブだけがローテーション処理を実行し、後続はローテーション完了後に起動するため、競合が起きません。
ローテーション処理は数秒で終わるため、過度に長くする必要はありません。ジョブにオフセット秒数を掛けて、起動を階段状にずらすのが手軽です。
対策③ リトライ機能の追加(主に落とし穴②に有効)
ログ出力先を分けても、共有される内部の状態ファイルなどに起因する落とし穴②のファイルロック解放待ちは完全には防げません。これらタイミング依存のエラーに対して、私はリトライを安全網として組み込みました。とくにファイルロックのエラーは、リトライが実質的に唯一の現実的な回避策でした。
snow コマンドが異常終了(終了コードが 0 以外)した場合に、数秒待って再実行する処理を数回まで繰り返します。リトライ間隔を少しずつ延ばす(バックオフ)と、競合が続く状況でもシステム全体への負荷を抑えられます。
⚠️ ただし、リトライ対象のエラーは慎重に選ぶ必要があります。今回の落とし穴①②はいずれも「SQL が実行される前に落ちる一時エラー」なので再実行しても安全ですが、
INSERTのような冪等でない処理では「成功していたかもしれない処理」を再実行すると重複データを生みます。リトライ対象の絞り込み方は、続編「リトライを“全エラー一律”にするな」で詳しく扱っています。
まとめ
Snowflake CLI を並列実行する際に、私がつかんだポイントを整理します。
-
接続設定は
~/.snowflake/config.tomlに定義する。認証方式は複数あり、今回はトークンで自動化しやすい PAT を使った(Key-Pair など他の方式は用途に応じて選べばよい)。 - 並列実行では2種類の競合エラーに注意する。落とし穴①:日次初回のログローテーション競合と、落とし穴②:ファイルロックを解放できないエラー。
- 対策は3つ。対策①ログファイルの分割(根本対策。主に①に有効)、対策②起動タイミングの分散(主に①に有効)、対策③リトライ(落とし穴②の安全網。タイミング依存のエラーに有効)。ログを分けて競合の芽を断ち、残るタイミング依存のエラーをリトライで拾う、という重ね方が私の環境では安定しました。
同じ落とし穴は、他の CLI ツールを並列実行する場面でも起こり得ます。ログのローテート挙動を把握しておくと、原因不明のエラーに振り回される時間を大きく減らせます。