概要
- GitHubのAPIには 2種類のIDシステム が存在
- GraphQLの node ID とWeb URL用の database ID の違い
- node IDからdatabase IDを 簡単に抽出 する方法の発見
- MessagePack による新IDフォーマットの解読
- GitHub内部のID管理の 複雑さと移行の歴史
GitHubのIDシステム:GraphQL node IDとdatabase IDの違い
- Greptileの新機能開発中、 GitHub PRコメントへのリンク生成 に苦戦
- GraphQL APIが返す node ID(例:PRRC_kwDOL4aMSs6Tkzl8) を利用
- Web上のURLは database ID(整数値、例:2475899260) を要求
- node IDとdatabase IDは 異なるID体系 で管理
- 既存データの 大規模な移行やバックフィル が現実的でない課題
node IDからdatabase IDの抽出方法
- node IDは base64エンコード された値
- PRRC_の後ろ部分を base64デコード し、96ビット整数に変換
- 下位32ビットが database ID と一致することを発見
- Python例:
decoded & ((1 << 32) - 1)
- Python例:
- これにより マイグレーション不要 でdatabase IDを取得可能に
node IDの構造と64ビット部分の謎
- 96ビット中、 上位64ビット の用途が不明
- node IDはグローバルIDとして オブジェクト種別や所有リソース情報 を含む可能性
- 他のリポジトリやオブジェクト でフォーマットを調査
GitHubのレガシーIDフォーマット
- 古いリポジトリ(例:torvalds/linux)は 異なるbase64フォーマット を使用
- 例:
MDEwOlJlcG9zaXRvcnkyMzI1Mjk4→010:Repository2325298
- 例:
- 構造: [タイプ番号]:[タイプ名][database ID]
- tree等のgitオブジェクトは SHA情報も含む
- 新旧IDフォーマットの混在 が確認できた
新旧IDフォーマットの使い分け
- 新しいリポジトリやオブジェクト は新フォーマット
- 古いリポジトリ や一部オブジェクト(例:User)はレガシーIDを維持
- オブジェクト生成日 によってどちらのIDが使われるか決まる場合も
新フォーマットの詳細:MessagePackによるエンコード
- 新node IDは MessagePack で配列としてエンコード
- 例:
decode_new_node_id("PRRC_kwDOL4aMSs6Tkzl8")→[0, 47954445, 2475899260]- 1番目:バージョン識別子(推測)
- 2番目:リポジトリのdatabase ID
- 3番目:オブジェクトのdatabase ID
- オブジェクト種別によって配列長が異なる ケースあり
- database IDは 常に配列の最後の要素
実用的なdatabase ID抽出コード
-
Pythonでの抽出例:
import base64 import msgpack def node_id_to_database_id(node_id): prefix, encoded = node_id.split('_') packed = base64.b64decode(encoded) array = msgpack.unpackb(packed) return array[-1] -
これで pull requestコメント等のdatabase ID を簡単に取得可能
GitHubのIDシステム運用の所感
- IDの扱いは「不透明な文字列」として推奨 されるが、実際には内部構造あり
- MessagePackやbitmask での解析・抽出が可能
- GitHubエンジニアも 2つのIDシステムのサポートに苦労 していると推測
まとめ
- node IDとdatabase IDの違い を理解し、 簡単な変換手法 を発見
- MessagePackによる新IDフォーマット の仕組みを解明
- GitHubの ID管理の歴史的背景と現状の複雑さ を把握
- 開発現場での 実践的な知見 として活用可能