KiSAKU
技術系4分で読めます

長さが合っているのに、中身が壊れていた

公開

作った画像を、ブラウザ越しにCMSへ上げる必要がありました。一度に送れない大きさだったので、分割して送り、向こう側でつなぎ直しています。

つなぎ直した長さは、元のデータと1文字も違いませんでした。それでも上がった画像は真っ黒でした。

この記事で扱うこと

長さが合っているのに中身が壊れていた原因と、それを見つけるまでに遠回りしたこと。そして分割して送るときに、検証をどこに置くべきかです。

長さが合うことは、無事の証明にならない

最初に確認したのは長さでした。送った文字数と受け取った文字数が一致していたので、無事に届いたと判断しました。

これが間違いです。どこかで1文字増えて、どこかで1文字減れば、長さは変わりません。実際に起きていたのはこれでした。

確かめやすいことを、確かめたいことの代わりにしていた

確かめたかったのは「中身が同じか」です。確かめたのは「長さが同じか」でした。長さは数えるだけで済むので、つい代わりに使ってしまいました。

長さが違えば壊れていると分かりますが、長さが同じでも壊れていないとは言えません。片方向にしか使えない検査を、両方向に使っていました。

どこで壊れたのかが分からない

次に困ったのが、壊れた場所の特定です。

全体をひとつの塊として扱っていたので、「どこかが壊れている」以上のことが分かりません。やり直すとしたら全体をやり直すしかなく、しかも同じ場所でまた壊れるかどうかは運任せになります。

実際、一度送り直しても同じように壊れました。何が起きているか分からないまま、同じ操作を繰り返していました。

分割して送るなら、検証も分割する

やり方を変えました。送る側で分割したときに、各かたまりごとに短い検査値を計算して一緒に渡します。受け取る側は、つなぐ前にその場で照合します。

全体だけを検証 かたまりごとに検証
壊れたことに気づける すべて送り終えたあと そのかたまりを受け取った時点
壊れた場所 分からない そのかたまりだと確定する
やり直す範囲 全体 そのかたまりだけ

これに変えた途端、壊れている場所が一発で出ました。しかも受け取り側が拒否を返すので、壊れたまま先へ進むことがなくなります。

やり直しの範囲は、検証の粒度で決まる

検証を全体にしか置かないと、失敗も全体になります。細かく検証すれば、失敗も細かくなります。

分割する目的は「一度に送れないから」でしたが、分割したことの利点は、失敗を小さくできることのほうにありました。

検証は、転送の「あと」ではなく「一部」にする

ここが今回の考え方の変化でした。

最初は、送り終えてから確かめる、という順番で組んでいました。転送と検証が別の工程になっていて、間に「壊れたまま進む余地」があります。

いまは、検査値が合わないかたまりは受け取らない形にしています。検証は転送の後工程ではなく、転送の一部です。こうすると「受け取れた」と「正しい」が同じ意味になります。

形式として正しくても、中身が正しいとは限らない

もうひとつ、別の層の話があります。

検査値が合って、データが完全に届いた。そこまで確認しても、上がった画像が意図したものかは、まだ分かりません。実際、最初に壊れた画像は「画像ファイルとしては正しい形式」で、開くこともできました。中身が真っ黒だっただけです。

そこで、送り先に登録する前に、実際に画素を読むようにしました。画面には出さず、いくつかの座標の色を取って、想定した配色になっているかを見ます。真っ黒なら、そこで止まります。

出す前に、出すものを見る

「形式として妥当か」と「意味として正しいか」は別の検査です。前者は自動で分かりますが、後者は中身を見に行かないと分かりません。

面倒に見えますが、やっているのは数点の色を読むだけです。壊れたものを世に出してから気づくコストと比べれば、はるかに安く済みます。

今回の学び

まず、長さの一致は、内容の一致ではないこと。確かめやすい指標を、確かめたい事実の代わりに使うと、一致しているのに壊れている状態を見逃します。

次に、失敗の大きさは、検証をどの粒度に置いたかで決まること。全体にしか検証を置かなければ、やり直しも全体になり、原因も特定できません。分けて送るなら、分けて確かめるべきでした。

そして、検証は工程の後ろに置くと、壊れたまま進む余地が残ること。転送の一部にしてしまえば、受け取れたことがそのまま正しさの証明になります。順番を変えただけで、確認という作業そのものが要らなくなりました。

最後に、形式の正しさと意味の正しさは、別々に確かめる必要があること。壊れた画像は画像として開けました。開けることと、意図したものであることは、まったく別の話でした。

← ブログ一覧へ戻る