「同じ応募ページ」なら「同じ店」と決めつけていた:店名が別の店になった記事 52 件
トレカダッシュ 開発の記録 / 2026年9月
iOS アプリ「トレカダッシュ」で、「利用者には、違う店の抽選に見える」という不具合が見つかりました。仙台駅前店の記事が「新宿南口店」と表示され、八王子本店の記事が「能代店」と表示されていました。52 件でした。
原因は、「同じ応募ページなら、同じ店だ」という、前提の間違いでした。
数字は、2026 年 9 月 7 日の作業の記録から書いています。
症状:店名が、別の店の名前になっていた
同じチェーンの、別の支店の名前が、記事に書かれていました。利用者から見ると、「仙台の店の抽選」のつもりで開いたら、「新宿の店」と書いてある、という状態です。52 件が、そうなっていました。
真因:応募ページが同じでも、店は同じとは限らない
「同じ応募ページを持つ記事の、店名と締切を、そろえる」処理がありました。同じ抽選なら、締切も同じ。同じ店なら、名前も同じ。そう考えて、グループの中でいちばん質の高い記事の店名を、全員に書き写していました。
この前提が、成り立っていませんでした。実データで数えると、こうでした。
| 応募ページ | 共有している店 |
|---|---|
| ある応募ページ(1) | 15 の別々の店(仙台、浜松、前橋、紫波、蒲郡、広島、など) |
| ある応募ページ(2) | 同じチェーンの9 支店 |
| ある応募ページ(3) | ある店と、別のチェーン 3 つ(チェーンすら別) |
チェーン全体で 1 つの応募ページを使う店は、普通にあります。同じページでも、店はそれぞれ違います。
この種の事故は、隠す側では、すでに一度起きていて、そちらには関門が入っていました(あるチェーンの 51 支店の件)。ところが、店名を書き写す側には、入れ忘れていました。同じ判定を使う、別の入口に、同じ修正を当てていなかったのです。
「座標が食い違う」だけの関門では、足りなかった
最初は、「座標(店の位置)が食い違うなら、別の店」という関門で守ろうとしました。ところが、片方の記事しか座標を持っていない組では、「確実に別の場所」の判定が false になり、素通りしてしまいます。実際に、書き込まずに結果だけを見る下見(ドライラン)で、ある 3 つの別のチェーンの店が、全部「晴れる屋 2 秋葉原タワー店」に書き換わるところでした。
恒久対策は、判定の向きを逆にすることでした。「別の店だと確かめられたとき、そろえない」ではなく、「同じ場所だと確かめられたときだけ、そろえる」。両方に座標があり、一致しているときだけ、そろえます。座標が無い、または食い違う組は、「同じ店だと分からない」ので、各記事が自分で名乗る名前を尊重します。締切は、同じ応募ページの同じ抽選なので、今までどおりそろえます。
すでにできている 52 件を、戻した
書き換えられた店名の、元の値は、保存していませんでした。手がかりは、この処理が一度も触らない項目(category)でした。記事が作られたときに、その記事自身の出どころ(店の X アカウント、まとめの行)から入ります。52 件をすべて目で確かめると、category のほうが、出どころと一致していました。
座標は、根拠にできませんでした。座標は店名から引き直す仕組みなので、店名が書き換えられたあとは、間違った名前の座標になっています(category は札幌駅前店なのに、座標は新宿)。だから、戻すときに座標も捨てて、引き直しの対象に戻しました。
- 下見で 51 件、実行。
- 実行後、新たな上書きは 0 件(関門が効いている)。
- 表示中で、店名と category が食い違う記事は 2 件だけ。どちらも、同じ店の書き方の違いでした。
ついでに見つかった、「行列に並べない」バグ
座標を引き直そうとしたら、4 件とも、順番が回ってきませんでした。位置情報を直す処理には、列が 2 つありました。
| 列 | 中身 |
|---|---|
| 正面(60 件ずつ) | 座標がほしい記事 |
| 再挑戦(300 件の窓) | 実測 658 件。うち、座標が本当に無いのは34 件だけ |
再挑戦の列に、解決できた記事まで入れていました(成功したときに、印を「false」に書いていたため、列に残る)。624 件の「もう用のない記事」が、窓を埋めて、本当に必要な 34 件に、順番が回りませんでした。
直し方は、解決できたら、印そのものを消すことです。どちらの列にも並びません。
分かったこと
「同じ」と決めつける前に、「同じ」だと確かめる。
- リンクが同じでも、店は同じとは限らない。共有のページは普通にあります。
- 揃えるのは、同じだと確かめられたときだけ。確かめられないときは、触らない。
- 同じ事故の修正は、同じ判定を使う、すべての入口に。片方にだけ入れると、もう片方で起きます。
- 元の値を保存していないときは、触っていない項目から復元する。ただし、その項目の裏づけを、1 件ずつ目で確かめる。
- 印を「false」にするのは、消すのとは違う。列に残り続けます。
この話に出てくるもの
- トレカダッシュ — トレカの抽選・予約・先着の情報を集める iPhone アプリ
- 1 つの記事を商品ごとに分ける処理で起きた 3 つの取り違え — 共有リンクが別の店の親を指した話
- 本物の抽選を、静かに捨てていた 4 つの実例 — 画面に出ない失敗の実例
ほかの記録
- 先着が始まった瞬間、サイトが順番待ちのページになる:8,000 バイトの関門を、15KB の案内ページがすり抜けた — イオンスタイルオンラインで先着が始まると、全体が「アクセス集中」の 15KB のページになった。同じ日の先に入れた 8,000 バイト未満の関門をすり抜け、「成功・商品 0 件」と読まれていた。題名で見る関門に直し、Amazon の 13 分が直列の AI 審査だったことも分かった。
- 同時実行の枠を増やす前に見るもの:2 コアのサーバーで、枠を 8 から 14 にした判断と、半分間違っていた前の結論 — 先着の監視を速くしようと間隔を詰めると、1 分おきの瞬殺監視が枠の順番待ちで遅くなった。枠を 8、11、14 と変えて測った。上限の正体は CPU ではなく、楽天 API の 1.5 秒待ちで枠を握る処理だった。
- 競合サイトと突き合わせて、取りこぼしを数えた:最初の「カバー率 23%」は測り方の間違いで、本当は 49% だった — 他のサイトが出している抽選を、こちらが出せているかを数えた。最初の 23% は、商品名の書き方の違いと期限切れの記事で歪んでいた。直して 49%、穴埋めの 5 つの問題を直して 90% に。規約で自動取得が禁止のサイトは取り込まなかった。
コメント
感想や、記事の間違いの指摘は、ここへ。書くには、GitHub のアカウントが要ります。
押すと、giscus(giscus.app)と GitHub に接続します。押さなければ、接続しません。詳しくはあつかいに書いています。