そこで私は、Web アプリケーション内から Lucene.Net インデックスの検索と書き込みを実装するための最良の方法についていくつかの調査を行ってきました。私は次の要件を設定しました。
- インデックスの同時検索とアクセスを許可する必要がある (クエリは並行して実行される)
- 複数のインデックスがあります
- インデックス検索を完全に最新 (「リアルタイム」) にする必要はありません
- 一定の頻度でジョブを実行してインデックスを更新します (頻度はインデックスごとに異なります)
- 明らかに、これらすべてを lucene の「ベスト プラクティス」に従い、適切に実行およびスケーリングできる方法で実行したいと考えています。
私はいくつかの役立つリソースを見つけました。また、このような SO に関するいくつかの良い質問も見つけました。
ガイダンスとしてその投稿に従い、インデックスを管理するために構築されたラッパーの同時実行ディクショナリでシングルトン パターンを試すことにしました。
簡単にするために、1 つのインデックスのみを管理していると仮定します。この場合、ラッパーがシングルトンになる可能性があります。これは最終的に次のようになります。
public sealed class SingleIndexManager
{
private const string IndexDirectory = "C:\\IndexDirectory\\";
private const string IndexName = "test-index";
private static readonly Version _version = Version.LUCENE_29;
#region Singleton Behavior
private static volatile SingleIndexManager _instance;
private static object syncRoot = new Object();
public static SingleIndexManager Instance
{
get
{
if (_instance == null)
{
lock (syncRoot)
{
if (_instance == null)
_instance = new SingleIndexManager();
}
}
return _instance;
}
}
#endregion
private IndexWriter _writer;
private IndexSearcher _searcher;
private int _activeSearches = 0;
private int _activeWrites = 0;
private SingleIndexManager()
{
lock(syncRoot)
{
_writer = CreateWriter(); //hidden for sake of brevity
_searcher = new IndexSearcher(_writer.GetReader());
}
}
public List<Document> Search(Func<IndexSearcher,List<Document>> searchMethod)
{
lock(syncRoot)
{
if(_searcher != null && !_searcher.GetIndexReader().IsCurrent() && _activeSearches == 0)
{
_searcher.Close();
_searcher = null;
}
if(_searcher == null)
{
_searcher = new IndexSearcher((_writer ?? (_writer = CreateWriter())).GetReader());
}
}
List<Document> results;
Interlocked.Increment(ref _activeSearches);
try
{
results = searchMethod(_searcher);
}
finally
{
Interlocked.Decrement(ref _activeSearches);
}
return results;
}
public void Write(List<Document> docs)
{
lock(syncRoot)
{
if(_writer == null)
{
_writer = CreateWriter();
}
}
try
{
Interlocked.Increment(ref _activeWrites);
foreach (Document document in docs)
{
_writer.AddDocument(document, new StandardAnalyzer(_version));
}
}
finally
{
lock(syncRoot)
{
int writers = Interlocked.Decrement(ref _activeWrites);
if(writers == 0)
{
_writer.Close();
_writer = null;
}
}
}
}
}
理論的には、これにより、公開されている 2 つのメソッドがあり、ASP.NET Web アプリケーション内から問題なく呼び出すことができる、インデックス (ここでは "index-test" という名前) のスレッドセーフなシングルトン インスタンスが許可されるはずSearch()
ですWrite()
。スレッドセーフ?(これが間違っている場合は、お知らせください)。
今私に少し問題を引き起こしていることが1つありました:
これらのインスタンスをApplication_End
Global.asax.cs ファイルで適切に閉じて、IIS で Web アプリケーションを再起動する場合に write.lock の失敗などを繰り返さないようにするにはどうすればよいですか?
これまでのところ、私が考えることができるのは次のとおりです。
public void Close()
{
lock(syncRoot)
{
_searcher.Close();
_searcher.Dispose();
_searcher = null;
_writer.Close();
_writer.Dispose();
_writer = null;
}
}
でそれを呼び出しApplication_End
ますが、アクティブなサーチャーまたはライターがある場合、インデックスが破損することはありますか?
どんな助けや提案も大歓迎です。ありがとう。