PostgreSQL、libpq5.12.1、接続動作に影響する大きな変更点
最終更新日 2019年12月12日(木)
Table of Contents
PostgreSQL データベースとの通信に広く使用されるクライアントライブラリに大きな変更点があり、アプリケーションに影響が及ぶ可能性があります。この記事では、変更点、アプリケーションが影響を受ける場合の対処方法、この変更の影響を軽減するために実施された対応について説明します。
libpq5.12.1 以降が原因のデータベース接続エラーが発生している場合、PostgreSQL の設定を正しくない値に設定しようとしています。たとえば、connect_timeout
の値は整数であることが想定されますが、"5 seconds"
のように文字列を渡すと、値はエラーになります。アプリケーションを修正するには、正しくない値を特定して修正する必要があります。
Ruby on Rails の接続値の修正
Ruby on Rails アプリケーションでは、YAML を使用してデータベース設定を実行できるため、このエラーが発生しやすくなっています。アプリケーションで libpq5.12.1 を使用して接続できない場合、次のようなエラーが表示されることがあります。
PG::ConnectionBad (invalid integer value "15# seconds" for connection option "connect_timeout")
このエラーの最もよくある原因は、config/database.yml
ファイルで、ERB ブロックとコメントの間にスペースが入っていないことです。
たとえば、次の YAML を考えます。
connect_timeout: <%= ENV['DB_CONNECT_TIMEOUT'] || 5 %># seconds
これは connect_timeout: "5 # seconds"
という値になり、エラーが発生します。ERB ブロックの後ろにスペースを入れると、値がコメントとして解釈されます。次に例を示します。
connect_timeout: <%= ENV['DB_CONNECT_TIMEOUT'] || 5 %> # seconds, with a space after the ERB block
これは connect_timeout: 5
という値になります。
heroku run bash
セッションで、アプリケーションの接続値を表示できます。
$ rails c
> pp ActiveRecord::Base.connection_config
{:adapter=>"postgresql",
:username=>"<username>",
:password=>"<password>",
:port=>5432,
:database=>"<database>",
:host=>"<host>",
:connect_timeout=>"5# seconds, this value will raise an error"}
型が正しくない値がある場合は、config/database.yml
を調べて値を修正してください。YAML に問題がない場合、イニシャライザまたはその他の場所 (Puma の after_fork
ブロックなど) で設定されている可能性もあります。
libpq とは何ですか? なぜ 5.12.1 が必要なのですか?
libpq は、PostgreSQL ワイヤプロトコルを理解するクライアントライブラリです。他の言語によって使用されます。たとえば Ruby では、Active Record で pg
gem を使用して PostgreSQL サポートを強化します。pg
gem は (共有オブジェクトを介して) libpq クライアントライブラリにリンクします。Active Record が Postgres に接続しようとするときに pg
gem が使用され、この gem が libpq
バインディングを使用します。
libpq のバージョンは PostgreSQL のバージョンと相関しています。最近、PostgreSQL バージョン 12 がリリースされ、これにより libpq バージョン 5.12 が導入されました。PostgreSQL 12 を完全にサポートし、将来のセキュリティパッチをすべて適用するには、Heroku 上のすべてのオペレーティングシステムを最終的に libpq5.12.1 以降にアップグレードする必要があります。
libpq の 5.11 と 5.12 の間でどのような動作が変更されましたか?
libpq5.11 では、データベース接続設定に無効な値を渡すことができ、ライブラリはそれらの値を無視していました。たとえば、connect_timeout
の値は整数であることが想定されますが、"5 seconds"
のように文字列を渡した場合、値は無視されます。
この動作は libpq5.12 で変更されたため、無効な値をデータベース接続に渡すとエラーになります。
つまり、以前はエラーなしで実行できていたアプリケーションでも、現在では、データベースに接続しようとすると例外が発生する場合があります。
Heroku による問題特定と対応
libpq5.12 のこの大きな変更点は当初、libpq の Changelog ドキュメントに記載されていませんでした。結果として、このライブラリはスタックイメージのアップグレードの一環としてロールアウトされました。複数の顧客から問題が報告され、Heroku での調査が完了するまで変更を差し戻す措置を実施しました。
調査の一環として、改善が提案され、5.12.1 がリリースされました。今回は、この大きな変更点が Changelog エントリに文書化されています。
またスタックイメージにロールアウトするのではなく、Heroku の buildpack システムを使用した緩やかなロールアウトを代わりに使用できるという判断が下されました。buildpack を利用することにより、アプリケーションの新しいバージョンが有効になるまで接続エラー動作は発生しません。これにより、リリースフェーズでエラーを捕捉する機会が得られることに加え、本番環境で中断が発生しても開発者がその場にいて対応できることが保証されます。
buildpack を介した libpq5.12.1 のロールアウトは、影響を受ける顧客が、問題のない古いバージョンにアプリケーションをロールバックして本番環境のダウンタイムを回避できることも意味します。さらに、開発者は環境変数 HEROKU_SKIP_LIBPQ12=1
を設定して “オプトアウト” することもできます。
この緩やかなロールアウトに加えて、影響を受けることがわかっている少数のユーザーを事前に特定し、警告を発することができました。
今回の libpq の変更は多くのアプリケーションに影響するものではありませんが、Heroku では安定性を重視し、慎重に対応する予定です。
libpq5.12.1 がデフォルトの libpq クライアントバージョンになるのはいつですか?
buildpack を介したこのクライアントのロールアウトはあくまで一時的なものです。libpq のバージョンは 2020 年 1 月 2 日以降に 5.12.1 にアップグレードされます。その日付を過ぎると、HEROKU_SKIP_LIBPQ12
は効力を失い、アプリケーションの古いリリースバージョンにロールバックしても、アプリケーションが使用する libpq のバージョンは変わらなくなります。