====== PostgreSQLのDBドライバ使用時の「SSL error」はスレッド周りを見直す ======
超絶ハマったのでメモ。
QtのPostgreSQLドライバ(QSqlDatabaseの"QPSQL")で連続したクエリを大量に発行すると、ポスグレのログに以下のようなログが記録されて正しく実行されない現象に遭遇した。
2020-06-11 19:41:00.598 JST [2593] db@postgres STATEMENT: DEALLOCATE qpsqlpstmt_10
2020-06-11 19:41:00.633 JST [3924] db@postgres LOG: SSL error: decryption failed or bad record mac
2020-06-11 19:41:00.634 JST [3924] db@postgres LOG: could not receive data from client: Connection reset by peer
2020-06-11 19:41:00.634 JST [3924] db@postgres LOG: unexpected EOF on client connection with an open transaction
2020-06-11 19:41:00.645 JST [3925] db@postgres ERROR: invalid input syntax for type timestamp: " " at character 77
2020-06-11 19:41:00.645 JST [3925] db@postgres STATEMENT: EXECUTE qpsqlpstmt_16 (TIMESTAMP WITH TIME ZONE '2020-06-11T10:41:00.634Z', '[, )', FALSE, 0, 0, 0, 0, 0, 10, '', 0, 2020061110480001, 0, 1, 0, 10)
2020-06-11 19:41:00.646 JST [3925] db@postgres WARNING: there is no transaction in progress
2020-06-11 19:41:56.266 JST [12492] db@postgres LOG: SSL error: tlsv1 alert protocol version
2020-06-11 19:41:56.272 JST [12492] db@postgres LOG: could not receive data from client: Connection reset by peer
Qt側のログは消失してしまったが、''SSL SYSCALL Resource temporarily unavailable''や''SSL SYSCALL error: EOF detected''といったエラーが出てDBとのコネクションが破壊され、トランザクションに失敗したりとかそんな感じ。
両方のログから読み取れるのは、クエリ発行時にプログラムとポスグレ間のSSL接続が通信途中で切れたり、データが破壊され変なクエリを実行しようとしたり、SSLの認証に失敗したりしてる。どーしてそんなことが起こるの?
それらしい単語でググると、postgresのSSLを無効にして解決とか出てくるけど、それって何の解決にもなってなくね?っていう。自分が加えた変更以後に発生するようになったので、原因は間違いなく自分ってことだけはハッキリしてる( ˘ω˘ )。
DBドライバとコネクションについて調べ、コードを追ってみたところ、スレッドをまたいで同一のDBコネクションを使ってたのが原因のようだった。一般的に、1つのデータベースコネクションを複数のスレッドで操作するのは禁じ手だそうで、[[https://doc.qt.io/qt-5/threads-modules.html#threads-and-the-sql-module|Qtのドキュメント]]にも
A connection can only be used from within the thread that created it. Moving connections between threads or creating queries from a different thread is not supported. \\ //(訳:コネクションは生成されたスレッドからしか使用できない。コネクションのスレッドをまたぐ移動や異なるスレッドからのクエリ生成は非対応。)//という記述がある。でーびーしょしんしゃなのでしらんかった。 DB操作スレッドで作られ使われていたコネクションに対し、今回メインスレッドからクエリを発行する変更を加え、なおかつ、特定の操作を行うと両スレッドが入り乱れてコネクションを使うために、問題が顕在化したようだった。 というわけで、メインスレッド用に別途コネクションを用意し問題を回避した。解決ではなく回避なのは、かなりアレな設計で緊急措置的な対応だから…。元の構造がアレすぎていい対処方法が思いつかなかったの……。