すべてのGitHubオブジェクトには2つのIDがあります
概要
- 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管理の歴史的背景と現状の複雑さを把握
- 開発現場での実践的な知見として活用可能