PostgreSQL アダプタでバイト長基準のチャネル識別子ハッシュ化
Action Cable の PostgreSQL サブスクリプションアダプタは、チャネル名が PostgreSQL の識別子上限(63 バイト)を超える場合に SHA‑1 ハッシュで置き換えるよう変更されました。これにより、マルチバイト文字を含む長いチャネル名でも通知が正しく配信され、サイレントなメッセージロスや誤配信が防止されます。
背景
PostgreSQL の識別子は 63 バイト までしか許容されず、超過した場合は自動的に切り詰められます(NAMEDATALEN‑1)。従来の実装は String#size(文字数)で長さチェックを行っていたため、マルチバイト文字列が 63 文字以内でもバイト数が 63 バイトを超えるケースでハッシュ化が行われませんでした。この結果、LISTEN/NOTIFY が切り詰められた名前で通知を返し、登録時のキーと不一致になることで 2 種類の障害 が発生しました。
- サイレントメッセージロス: 長いマルチバイトチャネルの購読者はハッシュ化されずに登録される一方、通知は切り詰め後のキーで配信されるため、該当チャネルの購読者が全く受信できません。
- クロスストリーム配信: 別のチャネルが切り詰め後の同一キーで登録されている場合、意図しないチャネルへ通知が漏れることがあります。
上記の問題は、#28751 で報告された Action Cable のストリーム識別子が PostgreSQL の上限を超えるケースに類似しています。実際に Japanese 文字列(例: "あ" * 30 + "X")でテストすると、String#size ではハッシュ化されずに 91 バイトの名前が LISTEN され、PostgreSQL が自動で 63 バイトに切り詰めることが確認されています。
技術的な変更
actioncable/lib/action_cable/subscription_adapter/postgresql.rb の channel_identifier メソッドが次のように修正されました。
-def channel_identifier(channel)
- channel.size > 63 ? OpenSSL::Digest::SHA1.hexdigest(channel) : channel
-end
+def channel_identifier(channel)
+ # PostgreSQL identifiers are limited to NAMEDATALEN-1 (63) *bytes*, not characters.
+ # Hash on byte length so multibyte channel names that exceed the limit stay within it.
+ channel.bytesize > 63 ? OpenSSL::Digest::SHA1.hexdigest(channel) : channel
+end
主な変更点は channel.size → channel.bytesize の置き換えです。コメントが追加され、バイト単位で判定すべき根拠が明示されています。この修正により、マルチバイト文字列でもバイト数が 63 を超えると即座に SHA‑1 ハッシュ(40 バイト)へ変換され、PostgreSQL の上限を超えることはなくなります。ASCII 名はバイト数と文字数が一致するため、従来通りの動作が保持されます。
テストコードも actioncable/test/subscription_adapter/postgresql_test.rb に test_long_multibyte_identifiers が追加され、実際の LISTEN/NOTIFY を用いたエンドツーエンド検証が行われます。テストは長いマルチバイトチャネルと、その 63 バイトに切り詰められた短いチャネルの両方を購読し、長いチャネルへのブロードキャストが正しく自身のキューに届き、短いチャネルへは漏れないことを確認します。これにより、バイト長基準への変更が期待通り機能することが自動的に保証されます。
設計判断
バイト長でのハッシュ化判定 を採用した理由は、PostgreSQL がバイト単位で識別子を評価する点に完全に合わせることが最小限の侵入で済むからです。元々存在したハッシュガードは channel.size > 63 という文字数チェックでしたが、バイト単位へ置き換えるだけで既存の API(channel_identifier の公開インタフェース)は変わりません。したがって、外部からの呼び出しや既存の設定に対して 後方互換性が保たれ ます。
また、テストの追加 によって回帰防止が明示的に組み込まれました。バイト長判定はコード変更だけでなく、実際の PostgreSQL 接続を通す統合テストで検証されるため、将来的なリファクタリングや別アダプタへの流用時にも同様の問題が再発しにくくなります。これらの判断は、バグ修正と信頼性向上を同時に実現する、実務的かつ保守的なアプローチと言えます。
まとめ
Action Cable PostgreSQL アダプタは、String#bytesize による長さ判定へ置き換えることで、マルチバイトチャネル名のハッシュ化を正しく行うようになり、PostgreSQL の識別子切り詰めによるメッセージロスと誤配信を防止しました。最小限のコード変更と新規テストの追加により、既存機能への影響を抑えつつ信頼性が向上しています。