編集長の佐藤(http://cocky.exblog.jp/)です。
NaviPlusサーチでは検索のコアコンポーネントとしてSolrを使ってますが、そんな中で最近ハマった点を一つ。
サーチで最近商品マスタの取り込みをマルチスレッド化したついでに、レコメンドとの連携のところの処理を変更した際の話です。
新しい処理の中で内部でEmbeddedSolrServerを使うようにしたんですが、ハマったのはその初期化の部分。
※以下のコードは全てSolr 4.2.xがベースです。Solr 4.4以降だとCoreContainerの初期化方法が変更になってるのでご注意ください。
最初は
CoreContainer.Initializer initializer = new CoreContainer.Initializer(); CoreContainer container = initializer.initialize(); container.load(solrHome, f); solrServer = new EmbeddedSolrServer(container, aid);
みたいな感じでインスタンスを作ってたんですが、なぜか「メモリがリークする」「index作成中に他のcoreのreplicationが失敗する時がある」という問題が発生。なんでだろうと思ってCoreContainerのソースコードを見たら、
public static class Initializer { (中略) // core container instantiation public CoreContainer initialize() { CoreContainer cores = null; String solrHome = SolrResourceLoader.locateSolrHome(); File fconf = new File(solrHome, containerConfigFilename == null ? "solr.xml" : containerConfigFilename); log.info("looking for solr.xml: " + fconf.getAbsolutePath()); cores = new CoreContainer(solrHome); if (fconf.exists()) { cores.load(solrHome, fconf); } else { (中略) } containerConfigFilename = cores.getConfigFile().getName(); return cores; } }
ってことで、initialize()時に一度coreをロードしているため、呼び出し元でcontainer.load()すると二重ロードになってしまうことが発覚。
なのでcontainer.load()を外したんですが、それでも問題が完全には解消せず。
よくよく考えてみたら、initialize()中のload()だと、solr.xml内で「loadOnStartUp=”true”」かつ「transient=”false”」なcoreを全部読み込んでしまってることに気づき、余計なcoreを読み込まないようにすればいいのではと考えました。
というわけで最終的には、インスタンスの作成部分のコードを以下のようにして解決しました。
// CoreContainerを初期化(自分自身なんでinitialize()だけで十分) CoreContainer.Initializer initializer = new CoreContainer.Initializer(); CoreContainer container = initializer.initialize(); // 今回使うもの以外のcoreをCoreContainerから外してcloseする(他コアへの干渉を防ぐ) Collection<String> coreNames = container.getCoreNames(); for(String cn : coreNames) { if(!cn.equals(aid)) { SolrCore c = container.remove(cn); c.close(); } } // EmbeddedSolrServerのインスタンスを作る solrServer = new EmbeddedSolrServer(container, aid);
ポイントはcontainer.remove()した上で、返されたSolrCoreに対してclose()を実行する点でしょうか。
単にcontainer.remove()しただけではSolrCoreの参照数が減らないので、明示的にclose()を実行して参照数を減らしてやる必要があります。
こんな感じで最近はJavaと格闘する毎日です…。