Unknown Region

プログラムでハマったエラーとその解決方法についての備忘録メモ

【Java】snappy-javaで生成したsnappy圧縮ファイルが壊れる事象への対処

備忘録がてら記載。

まず[snappy-java]JavaSnappy圧縮を容易に行うためのモジュール。

github.com

この[snappy-java]を利用して生成したファイルを別システムに食わせるプログラムを作っていたのだが、受け取るプログラム側で以下のようなエラーに直面した。

[SnappyBlock: RawUncompress failed]

これはsnappy圧縮されたデータが正常に解凍できなかったということを意味している。

実は環境によって起きたり起きなかったりするので悩んでいたのだが、ようやく原因を突き止めた。

 

まずそれを説明するためには[snappy-java]の仕組みを先に説明しておく必要がある。

[snappy-java]は内部的にC++でのプログラムのコンパイルを実施する。

そしてJava側では[native]として、その生成物を実行して利用する仕組みになっている。

ただもし環境依存でC++でのコンパイルが失敗した場合には、それを利用するのを諦めて代わりにJavaで組まれたプログラムで肩代わりするという仕組みになっていた。

分かりやすく説明すると、以下の通りの構成である。

interface [SnappyApi] --implements--> SnappyNative, PureJavaSnappy

結論を言うと、問題のない環境では[SnappyNative]が、問題が起きた環境では[PureJavaSnappy]が使われていた。

問題が起きていた環境は[alpine]で、内部的には以下のエラーが発生していたのだ。

Caused by: java.lang.UnsatisfiedLinkError: /tmp/snappy-1.1.8-9651bf49-a0e3-4240-81f5-848a90902bc7-libsnappyjava.so: Error loading shared library ld-linux-x86-64.so.2: No such file or directory (needed by /tmp/snappy-1.1.8-9651bf49-a0e3-4240-81f5-848a90902bc7-libsnappyjava.so)
  at java.lang.ClassLoader$NativeLibrary.load(Native Method) ~[?:1.8.0_252]
  at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1946) ~[?:1.8.0_252]
  at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1828) ~[?:1.8.0_252]
  at java.lang.Runtime.load0(Runtime.java:809) ~[?:1.8.0_252]
  at java.lang.System.load(System.java:1088) ~[?:1.8.0_252]
  at org.xerial.snappy.SnappyLoader.loadNativeLibrary(SnappyLoader.java:198) ~[snappy-java-1.1.8.4.jar:1.1.8.4]

そこで以下の記事を元に、alpineコンパイルに必要な処置を実施した。

qiita.com

そうしたところ、無事に[SnappyNative]が利用できるようになり問題は解決した。

[snappy-java]Githubでの過去のissueのやりとりを見るに、恐らく[PureJavaSnappy]には一部スレッドセーフになっていない問題が潜んでいると思われる。

実は今回僕が作ったプログラムは非同期処理でsnappy圧縮を行うものであった。

(しかもほとんどのユーザが[SnappyNative]を利用するので、問題の報告数が少ないのではないだろうか)

 

一応参考までに、上記のjava.lang.UnsatisfiedLinkErrorを確認するためのプログラムを貼っておく。

(これをtry-catchするプログラムにして、throwableのログを出力させれば良い)

Method method = Arrays.stream(SnappyLoader.class.getDeclaredMethods())
    .filter(e -> "loadNativeLibrary".equals(e.getName()))
    .findFirst()
    .get();
method.setAccessible(true);
method.invoke(null);

 

連絡先: plugout777★yahoo.co.jp (クローラー対策のため★を@に変更してください)