「1 ページ = 1 案件」ではなかった:1 つのフォームで 6 商品を受け付ける店の記事が、リンクが同じというだけで消されていた
トレカダッシュ 開発の記録 / 2026年8月
iOS アプリ「トレカダッシュ」で、「複数の商品を受け付けている抽選なのに、1 件しか記事が無い」という報告が、2 回、来ました。1 回目に直したつもりでしたが、2 回目が来ました。
この記事は、その真因と、同じ時期に見つけた、「作った項目を、アプリが一度も読んでいなかった」話の記録です。
この記事は、2026 年 8 月 25 日の作業の記録から書いています。
真因:リンクだけで、重複と決めていた
1 回目の報告で、「リンクが同じだから、重複扱いされている」ところまでは、直しました。しかし、すでにある記事が、隠れたままだったので、同じ指摘を、また受けました。
犯人は、runHideDuplicateLinkArticles、つまり、リンクだけで、重複と判断するジョブでした。
実データは、こうでした。ある通販店は、1 つの Google フォームで、6 つの商品の抽選を受け付けています。6 件の記事は、全部、同じ URL です。このジョブが、そのうち 5 件を「重複」として、消していました。利用者からは、1 商品ぶんしか、見えません。
直し方は、まとめる単位を、リンクから、リンク + 商品に変えることでした。同じリンクでも、商品が違えば、別の案件です。商品が分かっていない記事どうしは、区別する手がかりが無いので、これまでどおり、リンクだけで、重複とみなします。
「1 ページ = 1 案件」という前提は、トレカの抽選では成り立ちません。1 つの申込みフォーム、1 つの告知のページで、複数の商品を受け付ける店は、普通にあります。
すでに隠されていた記事は、同じリンクの表示中の記事と、商品または題名が違うものに限って、表示に戻しました(7 件)。ある通販店の 5 件(6 商品すべてが表示される)、大手のゲーム店 1 件、別のチェーン 1 件です。
サーバが作った「応募枠」を、アプリが一度も読んでいなかった
8 月 23 日から、サーバーは、応募の条件を AI で読んで、「応募枠」(entryTiers)を取り出していました。「プレイヤーズクラブ:本人認証済みの枠と、未認証の枠」「A グループと B グループ」「ビックカメラの Suica カード会員限定」といったものです。実データにも、ちゃんと入っていました。
ところが、iOS のアプリが、この項目を、一度も読んでいませんでした。AI の費用だけを払って、利用者には、見えない状態です。アプリに、項目を足し、詳細の画面に、「応募枠」として表示するようにしました。
サーバーとアプリの項目の突き合わせは、コマンドで見つかります。アプリが読む項目を、ソースから機械的に抜き出し(grep)、Firestore の実データと比べます。
商品の一覧に、告知文が混ざっていた
「ONE PIECE カードゲームコーナー【事前入店申込・抽選】8 月 31 日(月)JUMP SHOP 東京駅店」のような、イベントの告知が、商品として登録されていました。
言い回しを並べる方式は、追いつきません。そこで、具体的な日付という、一般的な規則で弾きました。トレカの商品名に、「8 月 31 日」のような暦の日付は入りません。「Vol.3」「第 2 弾」「2025 年 19 号」は日付ではないので、影響しません。10 個のパターンで試して、食い違いは 0 件でした。すでにある 6 件を削除しました。
分かったこと
前提を疑う。作ったものが、使われているかを確かめる。
- 「1 ページ = 1 案件」を、前提にしない。1 つのフォームで、複数の商品を受け付ける店は、普通にあります。
- 直したら、すでにある記事も見る。入口だけを直しても、隠れた記事は戻りません。
- サーバが作った項目を、アプリが読んでいるか、突き合わせる。費用だけかかって、利用者に見えない、ということが起きます。
- 言い回しの一覧より、一般的な規則。具体的な日付が入っていれば、商品名ではない、のように。
この話に出てくるもの
- トレカダッシュ — トレカの抽選・予約・先着の情報を集める iPhone アプリ
- 「同じ応募ページ」なら「同じ店」と決めつけていた — 同じ形の、別の前提の間違い
- 1 つの記事を商品ごとに分ける処理で起きた 3 つの取り違え — リンクと商品の取り違え
- 関門を直しても、すでにあるデータは直らない — 直しても残る記事の話
ほかの記録
- 英語表記とカタカナ表記は、1 文字も重ならない:正しい照合 42 件を、指紋の関門が却下していた — 相場の紐付け率が 39% に低下。相場のサイトに商品はあったが、自社の英語表記とカタカナ表記では指紋が一致せず、26 時間で 42 件が却下されていた。関門は弱めず、この照合だけに狭い 2 つ目の判定を足した。
- タイムアウトを 3 回直しても止まらなかった:真犯人は、同じ鍵の中で動いていた 2 つ目の処理だった — 楽天ブックスの在庫チェックが 1 日 17 回タイムアウトした。取得の上限、1 件ぶんの上限と 2 回直しても止まらず、3 回目で、同じ鍵の中の別の処理(全件を取り直し、上限なし)が真犯人と分かった。
- ログに秘密のトークンが平文で残っていた:呼び忘れようがない唯一の出口で伏せる — 通信の部品のエラーメッセージに URL がそのまま入り、取得サービスのトークンがログに出ていた。前に作った伏字の関数は、明示的に呼ぶ場所にしか効かず、呼び元は 50 か所以上。ログの唯一の出口で伏せる形に直した。
コメント
感想や、記事の間違いの指摘は、ここへ。書くには、GitHub のアカウントが要ります。
押すと、giscus(giscus.app)と GitHub に接続します。押さなければ、接続しません。詳しくはあつかいに書いています。