【ShinのAI活動ログ】「現在ニュースを取得できませんでした」――AIニュースウィジェットが沈黙した日

目次

ある朝突然、自作ウィジェットが沈黙した。
半日かけた犯人探しの結末と3つの教訓

ある朝、いつものように自分のサイトを開いたら、トップページに鎮座しているはずの「最新AIニュース」コーナーが沈黙していました。

「現在ニュースを取得できませんでした。しばらくしてから再度ご確認ください。」

これは、PR TIMESのRSSからAI関連のプレスリリースを自動で拾ってくる、私自作のウィジェットです。
昨日までは何食わぬ顔で普通に動いていました。
もちろん、コードは一切いじっていません。それなのに、突如として沈黙してしまったんです。

「しばらくしてから」……と表示されているので、おとなしく待ってみましたが、一向に直る気配がありません。

ここから、まる半日を費やした私の「犯人探し」が始まりました。
結論から先に言ってしまうと、犯人は外部のせいでもサーバのせいでもなく

――「自分が書いた、たった数行のコード」でした。

同じ落とし穴にハマる(かもしれない)皆さんのために、事件の全貌を順を追ってご報告します!

容疑者①:PR TIMESがRSSの配信をやめた?

まず疑ったのは、ニュースの配信元です。
PR TIMES側でRSSの仕様を変えたか、あるいは配信自体をストップしたのではないか?

とりあえず、ブラウザでフィードのURL( https://prtimes.jp/index.rdf )を直接叩いてみました。

……すると、数百件のXMLデータがズラッと画面を埋め尽くしました。日付も今日のもの。
めちゃくちゃピンピンしています。

つまり、配信元は「シロ」。問題は「自分のサーバから見に行ったときだけ、なぜかデータが取れていない」ということになります。一気にこちらの身内の問題になってきました。

容疑者②:うちのサーバ、外に出られていない?

次に疑ったのは、「サーバから外部への通信がどこかで詰まっている説」です。
WordPressの「サイトヘルス」画面を開いて、くまなくチェックします。

  • ステータス → 良好。
  • REST APIも、ループバックも、外部通信も、赤い警告はゼロ。
  • cURLのバージョンも新しく、SSL(OpenSSL)も正常に動作しています。

通信環境はどこも壊れていませんし、証明書エラーの線も消えました。ということで、サーバも「シロ」。

容疑者③:セキュリティ系プラグインやWAFの過剰防衛?

となれば、次はセキュリティ関連です。ログイン保護プラグインの設定を開いてみます。
しかし、中身はあくまで「外から入ってくる攻撃を防ぐ」機能ばかり。サーバ発の外向き通信には一切干渉していませんでした。こちらも「シロ」。

さらに、WAF(サーバ側のファイアウォール)も疑いました。
過去にPHPファイルを保存しようとして誤検知を食らった「前科」があるからです。
しかし、WAFを一時的に切ってみても状況は変わらず……。WAFも「シロ」でした。

……おかしい。容疑者が全員シロになってしまいました。
じゃあ、犯人はどこに……


決定的証拠:エラーログが「空っぽ」だった

万策尽きかけながら、サーバのPHPエラーログを開きました。
もし外部からのデータ取得(fetch)が失敗しているなら、タイムアウトなりSSLエラーなり、何かしらの断末魔(理由)が刻まれているはずです。

祈るように開いたエラーログ。そこにあったのは――

真っ白。1行も記録がありませんでした。

でも、実はこれが「決定打」になったんです。
「エラーが出ていない」ということは、プログラム自体はエラーで落ちることなく、正常に完走しているということ。処理自体は失敗していないのに、結果がゼロ件になっている。

これはもう、外部要因のせいではありません。私が書いたロジックが根本的におかしいのです。


真犯人発覚:「空っぽの結果」を、ご丁寧に30分間キャッシュしていた

自作コードを舐め回すように読み返して、すぐに見つけました。真犯人を。

キャッシュを保存する1行が、なんと「取得成功・失敗の判定の“外”」に置かれていたのです。

【Before】問題のコード

// fetchが失敗して中身が空っぽでも、このまま下の行に進んでしまう
$all_items = array_slice($all_items, 0, 10);

// ↓ 成功・失敗を問わず、無条件で「30分間キャッシュに保存」!
set_transient($cache_key, $all_items, 30 * MINUTE_IN_SECONDS);
return $all_items;

これの何がマズイのかお分かりでしょうか?
一度なにかの拍子(一時的な通信エラーなど)でデータ取得が空振りすると、「空っぽの結果」をそのまま30分ぶん、キャッシュにがっちり焼き込んでしまうんです。

次の30分間は、キャッシュの中に「空の配列」が鎮座しているせいで、再取得の処理すら走りません。
即座に「取得できませんでした」という画面を返し続けることになります。
配信元のフィードが復活していようがお構いなし。

「一度コケたら、問答無用で30分ロック」

これが、ウィジェット沈黙の正体でした。


解決編:直し方は明快!コードをこう変えました

原因がわかれば、直し方はシンプルです。「成功したときだけ、キャッシュに焼く」。これに尽きます。
さらに今回は、直近の成功分をバックアップしておき、万が一失敗したときは「多少古くてもバックアップを表示して、画面が真っ白になるのを防ぐ」という安全策も追加しました。

【After】修正後のコード(要点)

// ① 成功時のみ、正規キャッシュ+バックアップを更新
if ($fetch_ok && !empty($all_items)) {
    set_transient($cache_key,  $all_items, 30 * MINUTE_IN_SECONDS);
    set_transient($backup_key, $all_items, 7  * DAY_IN_SECONDS); // バックアップは長めに保持
    return $all_items;
}

// ② 失敗時:直近の成功分(バックアップ)があれば、古くてもそれを出して凌ぐ
$backup = get_transient($backup_key);
if ($backup !== false && !empty($backup)) {
    // 復旧チェックのため、次回取得までのキャッシュ時間は短めの5分に
    set_transient($cache_key, $backup, 5 * MINUTE_IN_SECONDS);
    return $backup;
}

// ③ バックアップすら無いときだけ「空」を返す。
// ただし、負のキャッシュ(空っぽの記憶)は5分で破棄!(30分もロックしない)
set_transient($cache_key, array(), 5 * MINUTE_IN_SECONDS);
return array();

あわせて、キャッシュキーの名前を _v1 から _v2 に変更しました。これで、居座っていた「腐った空のキャッシュ」は参照されなくなり、自動で一掃されます。わざわざ手動でキャッシュを消す手間も省けました。

コードをサーバにアップし、トップページをスーパーリロードすると……。

📌 最新AIニュース
・グロースエクスパートナーズ、自社ポータルにAIイントラネット…
・【YOKOHAMA Hack!】市民の声をAIで…
・AI翻訳「Blue One」、Slatorのリストに日本企業として唯一選出

復活しました!!
半日におよぶ壮大な犯人探しの結末がこれです。


読者の皆様へ:今回の3つの教訓

最後に、今回の事件から得た教訓をまとめます。皆さんのトラブルシューティングの参考になれば幸いです。

  1. 「外が壊れた」と思ったら、まず外を直接叩いて確かめる
    フィードのURLをブラウザで開くだけで、配信元の生死は一瞬で分かります。
    最初にこれをやっていれば、容疑者を一気に絞り込めました。
    推測する前に、まず一次情報に当たりましょう。
  2. エラーログが「空」なのも、立派な証拠
    「ログに何も無い=処理自体は失敗せず完走している」という消去法が使えます。
    これで、通信エラーなどの外部要因を全部切り捨てることができました。
    「なにもない」という事実が教えてくれることもあります。
  3. キャッシュは「失敗を焼かない」
    取得が空のときにキャッシュへ書き込むと、その失敗状態が長時間固定化されてしまいます。
    「成功時のみ保存」が鉄則です。
    (※また、PHPの @ などを使ったエラーの握り潰しも、原因究明を妨げるのでほどほどにしましょう!)

派手なサーバ障害でも、サイバー攻撃でもなく、真犯人は「自分の書いたたった数行のコード」。
エンジニアのトラブルシューティングのオチなんて、だいたいこういうものですよね。

皆さんも、キャッシュの取り扱いにはくれぐれもご注意ください!

Shin

Shin / クリエイター

× Shin’s GenAI ×

ひとりでは、できなかった。
AIと一緒なら、できた。

生成AIと対話しながら、設計・実装を一緒に進めた記録です。

Lux

Lux / AI妖精

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

IT・セキュリティ・財務の資格を持つオールドミドル。
器用貧乏、週末は大抵疲れ気味。
漫画・アニメをこよなく愛し、週末は900ccバイクで風を切る。
おまけで大の戦国ファン
【プロフィール】
資格:IT・セキュリティ国家資格 / 財務資格保持者
趣味:バイク(900cc)/ 漫画・アニメ / 動画制作
性格:器用貧乏・熱中しては疲れる
口癖:「なんとでもするさ」

コメント

コメントする

CAPTCHA


目次