OLTA TECH BLOG

テクノロジーと好奇心で事業を成長させる

TECH BLOG

ULID移行から最適な自己流IDの設計まで・その3【選択編】:ULID移行時に整数型IDと入れ替わるべきか?共存させるべきか?

 

◇ アノキミモノガタリその参

アノ日のULID、キミとまた出会えるまで十億億回の輪廻のタイムスリップ、A.D.10889年まで。

君の名は、01FZG96YPZK4SANAG1ZM5T2K9Z、忘れないその目。

01FZG96YPZK4SANAG1ZM5T2K9Z のキミ、

一体どこだい?

ぼくも、コマちゃんも、キミに会えたいよ。

50年1回の輪廻。

ぼくは、すでに十億億回の輪廻のタイムスリップも、

何度も何度も、何度でも、

何度も、タイムループの中でキミを探していた。

はじめに

こんにちは、転生したら相変わらずエンジニアだった OLTA(オルタ)プロダクトグループ研究開発チームのタイトルの前置きがとにかく長い B.です。

INVOYカードの開発を担当しています。

今回、UUID*1RFCRFC4122からRFC9562に刷新されたことをきっかけに、約2年間眠っていたテックブログの記事を加筆して発表しようと思います。

基礎編では、全面的に整数型ID、Snowflake ID、MongoDBのObjectID、ULIDまたUUIDなどの特徴、比較と選択について詳しく述べました。選択編ではULID移行時に整数型IDと入れ替わるべきか?共存させるべきか?について詳しく説明します。

独立・共存問題:整数型IDと入れ替わるべきか?共存させるべきか?

INVOYの既存振込データの主キー (PK, Primary Key) を整数型からULIDへ移行する際に、実にはすごく悩んだ箇所ありました。

なぜなら、既存データの Django Model のPKは既に整数型自動採番を採用しました。そこで、新しく作った振込依頼のModel はそのまま完全にPKを ULID にしましたが、既存の振込データではすでに大量の本番環境のレコードを持っているために、PKをそのままULIDに移行すべきかどうか、すごく悩みやした。

最初、自分は統一感が一番大事という考えで、既存の振込データの整数型のPKを丸ごとで直接にULIDに変更しても良いと思いました。一方、テックリードの柴田さんからのご助言で考えは変わりました。既存ユーザーにとって、ユーザーが作った振込データのIDはいきなり変わったのは、逆に認知負荷を招致することになるかもしれません。基本的に、既存ユーザーが作ったデータを勝手に変更するのはよろしくないという意見です。

また、ユーザーに送信した通知メールにある振込データのURLはもう修正できません。もちろん、既存の通知メールにある整数型IDのURLリンクをクリックしたら、自動的にULIDのURLに遷移させることもできますが、振込データURLへの認知負荷を考えて、古いデータへのアクセスは整数型IDのままにしました。

そこで、振込データのPKを整数型からULIDに変更するのではなく、別々で ulid 領域を新設して、APIから振込データにアクセスする際に、自動的に整数型IDかULIDかを判定してからデータを取得するようにしました。また、もしULIDが存在している場合、整数型IDからはアクセスできないようにしました。

最終的に、すでにリリースした振込データ化関連のModelは、すべて整数型IDとULIDを共存させて、PKは整数型IDのままにしました。

ただし、ユーザーがすでに作成した既存データに対して、PKに整数型IDのみで、ulidカラムにはULIDを持たせないようにしました。つまり、既存の振込データに対して、ULIDをPKとするのでなく、単に表現形式の1つとして存在しました。

また、新しい振込依頼関連のModelは、ULIDだけをPKとして利用する方針に決まりました。

これで、トレードオフでユーザーへの既存データに対する認知負荷を軽減することを優先する目的で整数型IDULIDを共存させることができました。

今回、このような共存させる決断をした理由には、過去にユーザーに送信したもう修正できない振込データの通知メールが存在することに大にありました。

もし通知メールがなければ、トレードオフとして、恐らくユーザーへの認知負荷の影響の重みはいまより大幅に低下するので、丸ごとPKをULIDへ入れ替わることに決断する方向へ移動するかもしれません。

総じて基本、古い整数型IDをPKとするデータの負債がない場合、そのままPKをULIDにするのはおすすめです。一方、古いデータはすでに整数型IDをPKとして利用した場合、上記の共存案でご参考なれば幸いです。

整数型IDとULIDと共存させる時の利点と注意点

実には、もしPKを今までの整数型IDのままにして、ULIDと共存させる場合は、気づきにくい盲点は2つあります。

まずは、前述通りにULIDではsnowflakeのように専用な単調増加の番号を確保できる発行メカニズムがない限り、100%単調増加であることは保証できません。

しかし、PKを整数型IDのままにすることの意味は、シングルDBにおいては100%単調増加であることが保証されています。B+treeインデックスのレコードの検索や挿入において、最高の性能が得られます。

これは、使い方によって、利点とも言えるかもしれません。一方で、例えばDjangoの場合、もし64-bitBigAutoField(BigIntegerField)でなく、32-bitutoField(IntegerField)を利用している場合、整数型の最大値21億の制限に引っかかります。

つまり、結局、いずれ記録の数は億単位に達したら、PKをIntegerFieldから、BigIntegerFieldに移行するか、または完全にULIDに移行しなければならない日は必ず来ると考えられます。

さらに、もし複数のサーバーを跨いで、複数のDBで構成された分散システムの場合、同じDBにおけるシーケンス番号が枯渇する問題だけでなく、違うサーバーやDB間で発行された番号が衝突する問題も出始めます。そこで、専用な番号発行サーバーも必要となり、整数型自動採番は不都合になります。

従って、共存するか、潔くて完全にULIDに移行するのか、かなり事業の種類や規模また発展スピードによるタイムリーな正解がないことになります。

まとめ

以上は選択編の内容です。主にULID移行時に整数型IDと入れ替わるべきか?共存させるべきか?について話しました。次の工程編ではPythonにおいてULIDの実応用とか、実際のDjango/DRF工程において起こった様々な問題について詳しく述べます。

この記事、もしご参考になればとてもうれしいです。OLTAでは Tech Vision の元、一緒にユーザーに価値を提供し、その結果事業を成長させるサービス作り続けるための仲間を募集しています。 もし、この投稿にご興味を持っていただいたら、是非カジュアルにお話しさせてください。

corp.olta.co.jp

 

IDシリーズ記事の一覧

techblog.olta.co.jp