いろいろ備忘録日記

主に .NET とか Go とか Flutter とか Python絡みのメモを公開しています。

Npgsql.Netで巨大なバイナリデータをやり取りするとOutOfMemoryExceptionが発生する件について (Prepareメソッド, OutOfMemoryException, bytea型, 7.3以降)


ハマましたので、メモメモ。


巨大なバイナリデータ (50MB〜100MB)をbytea型の列を持つテーブルにINSERTしようとしたら
OutOfMemoryExceptionが発生していました。環境によっては、もっと少ないデータ量で出るかもしれません。


トレースを見てみると、どうもNpgsql.Net側でごにょごにょしている際に発生しているみたい。
(ToBinaryメソッド経由で文字列に変換してる??)
情報を探してみると、以下の記事が見つかりました。


同じ現象の人がいて、回答の中に”コマンドを実行する前にPrepareしとけ"と
記載されていました。で、自分のところにも

command.Prepare();
...
command.ExecuteNonQuery(...);

と設定してみたら、、、動いた!


データを読み出す部分でも、OutOfMemoryExceptionが発生していたのですが
これも解消されました。まさか、これで直るとは・・・・w


大きめのバイナリデータやデータを扱っている場合は、実行前に
Prepareしておくのが無難ですね。


尚、これが有効なのはバージョン7.3以降らしいです。
7.3より前のバージョンの場合、単純に無視されるとのこと。


ちなみに、Prepareメソッド自体はDbCommandクラスにて定義されているメソッドですので
どのプロバイダーのコマンドにもあります。

【追記】
以下のような情報も発見。


以下、上記ページより引用。

About technical details:

Bytea with unprepared statements take up too much space because the actual bytes have to be translated to string where each byte may be represented to 3 or 4 chars. By using prepared statements, Npgsql is able to send those bytes directly to postgresql without having to do the conversion.

For the method which translate bytes to bytea string, check internal static Object ToBinary(NpgsqlBackendTypeInfo TypeInfo, String BackendData, Int16 TypeSize, Int32 TypeModifier) method in NpgsqlTypes/NpgsqlTypeConverters.cs line 73.


Prepareしないステートメントにbyteaがあると、それを文字列に変換しようとするので、大量の容量が必要になる。
Prepareしてるステートメントの場合、Npgsqlは上記変換処理をせずに直接postgresqlに送ることが出来る。


なるほど、、、。



================================
過去の記事については、以下のページからご参照下さい。

サンプルコードは、以下の場所で公開しています。