公式ドキュメントは、出発点であって終着点ではない
この連載は、認証情報の使い分け、サイレント障害と続けてきた。通底しているのは、signal と現実のずれをどう埋めるか、という話だ。認証が通ったことは、意図した身分で通ったことを意味しない。処理が成功を返したことは、処理が効いたことを意味しない。今回扱うのは、その延長にある。仕様書に書いてあることは、実機がそう動くことの近似であって、保証ではない。
最初に一度だけ断っておく。これは特定のクラウドを責める話ではない。仕様書は人が書き、実装は別の時間に別の都合で動く。どんなに丁寧に書かれた仕様や型も、実機そのものではなく、その近似だ。だから仕様と実機のずれは、どのプラットフォームを使っていても起こり得る。以下の例はさくらのクラウドのものだが、同じ構造のずれは、他のクラウドでも出会う種類のものだ。
EastCloud ではさくらのクラウド上でアプリケーション実行基盤(PaaS)を開発している。以下は、その過程で実際に API を叩いて確かめた、仕様や signal と実機のずれである。3 つの差分を挙げ、そのうち一つについては、なぜ自分たちがそれを踏んだのかも書く。各差分には検証した時期を添える。なぜ時期を書くのかは、記事の終わりで触れる。仕様は変わり得るので、重要な判断の際は公式ドキュメントと各自の環境で改めて確認されたい。

差分 1:型のずれ(2026 年 5 月)
オブジェクトストレージのコントロール API(バケットやアクセスキーを管理する面)で、アクセスキーを発行する処理がある。その応答に、キーの id が入っている。
仕様書では、この id は 整数(64 ビット整数)と定義されている。だから素直に、整数として受け取る実装を書く。
ところが実機は、この id を 20 文字の英数字の文字列で返す(S3 互換のアクセスキー id の形式、たとえば EXAMPLEACCESSKEYID00 のようなもの)。整数型にデコードしようとする実装は——たとえば Go で int64 に受けると——作成が成功した 201 を受け取ったその瞬間に、パースで失敗する。
しかも、これは単に「読み取りで止まる」だけでは済まない。このキーのシークレットは、発行時の応答にしか含まれず、後から取り直せない。つまり応答のパースに失敗すると、キーは作られたのにシークレットを受け取れず、二度と使えないキーが残る。 「作成は成功したのに」という分かりにくさに加えて、使えない孤児キーが積もるという実害がある。
付け加えると、同じ仕様書の別の箇所——このキーを指定する URL のパラメータ——では、同じ id が文字列として定義されている。仕様書の中で型の表現が二通りあり、実機は文字列のほうと一致していた、ということだ。
学び:仕様書の型は、実機の型と一致するとは限らない。 とくに「作成に成功した直後、そのレスポンスを読む」経路は、型のずれが成功を失敗に化けさせる場所なので、実際の値を一度は目で見てから、受け取る型を決めると安全だ。
差分 2:ドキュメントに載っている経路が、想定どおりとは限らない(2026 年 6 月)
バケットが存在するかどうかを確認したい、という素朴な要求がある。ドキュメントを読むと、バケット名の下に plan という副リソースのパスがある。存在確認に GET で使えそうに見える。
ところが実機でこれを GET すると、405 Method Not Allowed が返る。 存在するバケットでも、しないバケットでも、一律 405。このパスは別の method で使うもので、GET で読む用途ではなかった。ドキュメントにパスがあることを根拠に GET を前提で実装すると、実機で初めて 405 に気づく。
正解は別の副リソース、usage のほうだった。存在するバケットなら 200、存在しないバケットなら 404 を返す。存在確認には、こちらを使う。
学び:ドキュメントにそのパスが載っていることと、想定した method でそれが読めることは、別である。 GET で状態を読むつもりの経路は、使い捨てのリソースで一度その method を叩いて、本当に読めることを確かめてから採用すると確実だ。
差分 3:成功は、構成の正しさを保証しない(2026 年 9 月)
これが、前回のサイレント障害の記事と地続きになる差分だ。
冗長化構成のデータベースを作るとき、ネットワークインターフェースの指定が独特な形をしている。配列なのだが、位置に意味があり、先頭の要素は空でなければならない。 仮想 IP は 2 番目に置く。プランの指定も、素直に書きたくなる場所ではなく、別の決まった場所に置く。生の wire(通信上で実際にやり取りされるデータの形)だけを見ても、どれが正しい並びなのかは分かりにくい。
ここで注意が要るのは、間違った形で作っても、作成 API が 201 を返すことだ。 しかもその後、可用性ステータスは available になり、インスタンスの状態は up になる。割り当てられた 2 つの実 IP には ping も通り、データベースのポートにも接続できる。ステータスを読む確認も、実 IP への疎通確認も、すべて通ってしまう。
構成が誤っていると分かるのは、仮想 IP への通信を試したときだけだった。正常なら、仮想 IP 宛ての ARP が主系の NIC の MAC アドレスで解決される。誤った構成だと、この ARP が解決しない(今回の環境での挙動で、ARP は同じ L2 セグメントからでないと観測できない)。実 IP やステータスだけを見るヘルスチェックは、すべて緑のまま通る。 仮想 IP 経由で疎通を試して、初めて壊れていると分かる。この挙動は、事業者にも確認している。
前回の記事で、「成功という signal は、試みたことの報告であって、効いたことの報告ではない」と書いた。これはその、クラウド API 側での一例である。作成が成功を返すことと、作ったものが意図どおり機能していることは、別のことだ。
学び:作成 API の成功と available は、構成が正しいことの証明ではない。 その構成が本当に意図どおり機能しているかは、構成とは独立した経路——この場合は、実 IP でもステータスでもなく、仮想 IP 経由の疎通——で確かめる必要がある。
なぜ、誤った形を作ってしまったのか
差分 3 には続きがある。「なぜ、その誤った並びを作ってしまったのか」という話で、ここは仕様や実機の側ではなく、こちらの側の話だ。
実は、さくらが配布している公式の Go ライブラリは、この位置依存をきちんと扱っている。高レベルの型には要素ごとに位置を指定するフィールドがあり、内部のシリアライズが、指定された位置に要素を置いて、それより前を null で埋めて送る。答えは、ライブラリの中にあった。 高レベルの型を使えば、利用者は位置を正しく指定するだけで、正しい wire が組み上がる。ただし、その位置の指定を忘れると、Go のゼロ値がそのまま有効な位置として送られ、高レベルの型を使っていても差分 3 と同じ誤った形になり得る。ゼロ値が「未指定」と区別されないのは、別の場面でも刺さる注意点だ。
では素直に高レベルの型を使えばよい、という話でもある。ただ、事情があって自前で wire を組むことはある。その場合、ライブラリがやってくれるはずの位置合わせを、自分で持つことになる。そして——これが痛いところだが——別のリソース(VPC ルータ)では「先頭は特別で null を置く」という同じ作法をすでにコードに書いてあったのに、データベースには引き継げていなかった。同じ作法を、片方では encode し、もう片方では落としていた。
ここに、表面と振る舞いの差がある。型のシグネチャや、wire の生の形だけを眺めていると、「位置に意味がある」という約束事は見えてこない。それは、シリアライズの実装や、隣のリソースのコードの中に書かれている。表面の形だけを読んで、振る舞いまで読まなかった。 差分 3 で作った誤った形は、この読み落としの産物であり、しかも実機がそれを 201 で受理したので、気づく機会はいっそう遠のいた。
学び:答えは、すでにライブラリや既存のコードの中にあることがある。 シグネチャや wire の表面だけでなく、その振る舞いまで読む。そして、表面に従って組んだものが正しいかどうかは、やはり実機で——意図どおり動くまで——確かめる。
仕様も実機も、時間で変わる
各差分に検証時期を添えた理由を書く。この記事自体が、その理由の実例になったからだ。
本稿には当初、データベースのプラン変更と復元に関する差分を、あと 2 つ入れる予定だった。どちらも、実機で確かめた内容だ。だが公開前に確かめ直すと、どちらも今の記事には使えなかった。しかも、使えなかった理由が、2 つで正反対だった。
プラン変更のほうは、6 月に挙動を確かめたあとに、その機能自体が一時的に停止されていた。観測のあとで、世界が動いていた。復元のほうは逆で、8 月に「API が見当たらない」と確かめたのに、任意の時点へ戻す機能(PITR)が、それより前から提供されていた。世界は動いていない。こちらが、別の場所を見落としていたのだ。
過去の観測は、この二通りで裏切る。あとから古くなるか、はじめから一面的か。どちらにしても、そのまま今の事実としては使えない。
同じことは第 1 弾でも起きた。あのとき「サービスプリンシパルではプロジェクトを作れない」と書こうとして、実機を叩き直したら、作れるようになっていた。階層構造の機能が、その少し前に提供されていたからだ。
だから、仕様と実機の差分を扱うときは、必ず検証の時期を書く。 そして、過去の観測を今の事実として断定しない。公式ドキュメントが実機とずれるのと同じくらい、自分の観測も、現在の——あるいは観測した当時の——実機とずれていることがある。
まとめ
公式ドキュメントは、API を組むときの出発点だ。信頼できる出発点である。ただ、終着点ではない。そして公式が配布する SDK には、ときに答えそのものが書かれている——表面だけを眺めていると、それに気づけない。
仕様書の型と実機の型が食い違うことがある。ドキュメントに載っているパスが、想定した method では読めないことがある。誤った構成でも作成は成功して available になり、実 IP やステータスを見る確認では壊れが見えないことがある。そして、その誤った構成を自分で作ってしまう原因は、ときに、答えが既にある場所——ライブラリの実装や、隣のリソースのコード——の表面だけを読んで、振る舞いまで読まないことにある。
防ぎ方は、連載を通じて同じだ。ドキュメントや型という表面を鵜呑みにせず、使い捨てのリソースで実機を叩き、型・経路・成功を自分の目で確かめる。 ライブラリや既存のコードに答えがないかは、シグネチャだけでなく振る舞いまで読む。そして、その確認には日付を付ける。仕様も実機も、確かめた瞬間の姿でしかないからだ。
公式ドキュメントは、実機という現実を写し取ろうとした、よくできた地図である。地図と現地のあいだのわずかな差分を、自分の足で歩いて埋める。それが、クラウドの上に基盤を作るという仕事の、地味だが本質的な部分である。
参考リンク
- データベース | さくらのクラウド マニュアル
- 【さくらのクラウド】機能アップデートに関するお知らせ(2026年3月9日) — 継続的バックアップ・PITR の追加
- データベースアプライアンス プラン変更機能の一時利用停止について(2026年7月23日)
- sacloud/iaas-api-go — 公式の Go ライブラリ
