GlobalID::Locatorにfetchメソッドを追加し、欠損と障害を区別できるように

rails/globalid

GlobalID::Locatorは、locate がブランクやパース失敗時に nil を返し、レコードが見つからない場合はバックエンド例外をそのまま伝搬させていました。この挙動では、削除済みレコードと一時的なバックエンド障害を呼び出し側が判別できませんでした。今回の PR は新メソッド GlobalID::Locator.fetch と2つの専用例外 RecordNotFound / RecordUnavailable を導入し、両者を明確に分離します。

背景

メール配信ジョブで一時的なデータベース接続エラーが起きた際、ActiveJob::DeserializationError が全ジョブを破棄してしまうケースが報告されていました。実際にはレコードが削除済みの場合だけ破棄したいが、バックエンド障害の場合はリトライが望ましいという要求がありました。既存の locate はバックエンド例外を直接スローするため、呼び出し側が例外クラスを知っていなければ判別できませんでした。

技術的な変更

GlobalID::Locator に共通基底例外 Error を追加し、RecordNotFoundRecordUnavailable がこのクラスを継承するようにしました。これにより、両例外を一括で捕捉できる統一インターフェースが提供されます。

fetch メソッドを実装し、内部で locate_many(ignore_missing: true) を呼び出してレコード取得を行います。取得結果が空の場合は RecordNotFound を、locate_many の呼び出し自体で例外が発生した場合は RecordUnavailable をそれぞれスローします。メソッドは nil を返すケース(ブランクやパース失敗)も従来通りに保持し、only: フィルタも引き継ぎます。

README へ使用例を追記し、fetch が成功、レコード削除、バックエンド障害の3つの結果をどのようにハンドリングすべきかを示しました。例示はインラインコードで示し、RecordNotFoundRecordUnavailableGlobalID::Locator::Error を継承している点を強調しています。

設計判断

バックエンド固有の例外クラスに依存せずに状態判別を GlobalID 側で完結させる方針が採用されました。これにより、アプリケーションはバックエンド実装の変更に影響されず、fetch の戻り値だけでリトライ戦略や破棄ロジックを決定できます。locate_manyignore_missing オプションをサポートしていることが前提となり、カスタムロケータは同オプションを実装する必要がありますが、既存ロケータへの影響は最小限です。

まとめ

GlobalID::Locator.fetch と専用例外の導入により、削除済みレコードと一時的障害を明確に区別でき、Active Job などのバックグラウンド処理で安全な破棄とリトライを実装できます。設計上は例外階層を統一し、バックエンド非依存の判別ロジックを提供した点が重要です。

記事メタデータ

Generated by:
gpt-oss-120b for DiffDaily
LLM Trace:
736c2242

この記事はAIによって自動生成されています。内容の正確性については、必ずソースコードやPRを確認してください。

品質レビュー結果

Review Status:
承認済み
Review Count:
1回
Reviewed by:
gpt-oss-120b for DiffDaily

Review Criteria:

記事構成 ✓ PASS

Title, Context, Technical Detailの存在と明確さ

リード文がタイトル直下にあり、背景・技術的変更・設計判断・まとめの各セクションで構成されている。結論部はリード文の単なる繰り返しではなく追加の意義を示している。

カスタムMarkdown構文 ✓ PASS

シンタックスハイライト・GitHubリンク記法の正確性

コードブロックは使用されていないが、Markdown記法自体に誤りはなし。PRリンクは正しい形式で記載されている。

対象読者への適合性 ✓ PASS

エンジニア向けの適切な技術レベルと表現

技術的な内容が中心で、エンジニア向けの記述になっている。初心者向けの過剰な解説はない。

パラグラフ・ライティング ⚠ WARNING

トピックセンテンス・1段落1トピック・段落長

各セクションの冒頭文はトピックセンテンスとして機能しているが、箇条書き部分がトピックセンテンスなしで列挙されており、1段落1トピックの原則を一部満たしていない。

Diff内容との照合 ⚠ WARNING

コードブロックとDiff内容の一致

記事中では `fetch` が内部で `locate_many(ignore_missing: true)` を呼び出すと記載されているが、提供された Diff では `fetch` は `fetch_record` を呼び出す実装になっている。実装詳細の食い違いがあるため注意が必要。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

使用されている技術用語(RecordNotFound、RecordUnavailable、Error 等)は PR 内容と一致し、誤用は見られない。

説明の技術的正確性 ⚠ WARNING

技術的主張の正確性と論理性

`fetch` が `locate_many` を利用する旨の説明が Diff と完全に合致していない点があり、技術的説明としての正確性にやや欠ける。

事実の突合 ✓ PASS

PR情報による主張の裏付け(ハルシネーション検出)

記事内の主張は PR の背景・目的と整合しており、根拠のない推測や外部知識の付与はない。

数値・固有名詞の確認 ✓ PASS

PR番号・コミットID・バージョン等の正確性

数値や固有名詞の記載はなく、誤りも見受けられない。

タイトル・説明との一致 ✓ PASS

記事タイトル・説明とPR内容の一致

記事タイトルは PR の内容(fetch メソッドの追加とエラーの区別)を正確に日本語で要約している。

外部知識の正確性 ✓ PASS

PRに記載のない外部知識(LTS、サポート状況など)の不使用

PR に記載されていない外部情報(バージョンサポート状況等)は記事に含まれていない。

時間表現の正確性 ✓ PASS

時間表現がPR情報と一致しているか

時間表現の誤りはなく、PR の表現と一致している。