RedshiftとPostgresqlに同時アクセスした際に発生したエラーの解決方法
はじめに
DI部のおおたきです。Talendを使ってRedshiftとRDS(Postgresql)の両方に接続するジョブを作成したのですが、Postgresqlへの登録時にエラーが発生したので対応方法を備忘録として記載しました。
開発環境
今回の開発環境です。
- Windows7
- Talend6.4.3
登録エラー内容
どんなエラーが発生したかというとPostgresqlに対してCOPY文を使ってCSVデータを登録しようとしたところ、以下のエラーが発生してしまいました。エラー内容からするとRedshiftのドライバが使用されておりクラスがキャストできないという内容です。
java.lang.ClassCastException: com.amazon.redshift.core.jdbc41.S41NotifiedConnection cannot be cast to org.postgresql.core.BaseConnection
Postgresqlのコネクションコンポーネントを使用しているのに謎なエラーです。
サンプルジョブを作成して検証
以下のサンプルジョブを作成して検証してみました。
サンプルジョブではPostgresql⇨Redshiftの順番にコネクションを生成しているだけのジョブです。
Log1及びLog2はコネクションのクラスを標準出力させています。
実行してみると、以下のようなログが出力されました。
tPostgresqlConnection_1:class com.amazon.redshift.core.jdbc41.S41NotifiedConnection tRedshiftConnection_1: class com.amazon.redshift.core.jdbc41.S41NotifiedConnection
想定では1行目のログではPostgresqlのコネクションクラスが表示されるはずでしたが、Redshiftのコネクションクラスが表示されています。この現象が原因でエラーが発生していたのがわかりました。
何でこんなことになるのか?
最初はTalendのバグなのかと思ったのですが、色々調べたところRedshiftのJDBCドライバーの名前空間の扱いの問題だということが分かりました。
ここからはTalnedというか完全にJavaの世界のお話になってしまいますが原因としては以下のようになります。
Java(Talend)を実行するとPostgresqlとRedshiftドライバのjarはDriverManagerクラスに登録されます。登録される順番はクラスパスの順番に影響されるようです。
次にコネクションを取得するのにDriverManager.getConnection()を呼び出しますが、引数にURLを指定します。URLは「jdbc:redshift:エンドポイント:ポート/データベース名」みたいなJavaエンジニアでしたらおなじみのURLです。この引数を元にDriverManagerクラスは登録されたドライバ順に適切なドライバを探し見つかったらそのドライバクラスを返却します。
仮にRedshiftのドライバが先に登録されていている場合、Postgresqlに接続しようとして「jdbc:postgresql:エンドポイント:ポート/データベース名」をURLの引数を指定した場合でもRedshiftのドライバが返却されてしまいます。これはこちらのRedshiftのページに記載があります。
「jdbc:postgresql://エンドポイント:ポート/データベース という旧形式で指定されている JDBC URL は、まだ動作します。」
そのためPostgresqlのコネクションコンポーネントを使用しても、Redshiftのドライバが返却されてしまっていた訳です。
解決方法は?
じゃ、どうやってこの問題を解決させるかというと、こちらもRedshiftのページに記載がありました。こちらのRedshiftのjdbcオプション一覧に「OpenSourceSubProtocolOverride」というオプションが存在します。
有効になっている場合、この設定は、Amazon Redshift JDBC ドライバと PostgreSQL JDBC ドライバとの間に発生する可能性のある競合を防ぎます。場合によっては、アプリケーションは PostgreSQL JDBC ドライバを使用して Amazon Redshift JDBC ドライバおよびその他のデータソースに同時に接続することがあります。この場合、PostgreSQL データソースに接続するために使用するこの接続属性を、JDBC URL に追加します。以下の値を指定できます。
・true—OpenSourceSubProtocolOverride を有効にします。
・false—OpenSourceSubProtocolOverride を無効にします。
デフォルトはfalseです。
ではオプションを指定してみます。データベース名の後ろに指定します。
再度実行します。先ほどと結果が代わりPostgresqlのコネクションが正しく取得できました。
tPostgresqlConnection_1:class org.postgresql.jdbc4.Jdbc4Connection tRedshiftConnection_1: class com.amazon.redshift.core.jdbc41.S41NotifiedConnection
まとめ
今回、発生したエラーはTalendの問題ではなくRedshiftのJDBCドライバーはRedshiftとPostgresSQLのどちらの名前空間でも接続できるというものが原因でした。したがってTalendだけでなくJavaプログラムで実装した際にも役立つ内容かと思います。
今回は以上です。