Синхронизация и файл ввода-вывода - PullRequest
1 голос
/ 23 марта 2012

У меня есть метод, который использует индекс, расположенный в определенном каталоге.

public class TestSearchEngine implements SearchEngine<Tag> {

private static final String INDEX_PATH = "/test/index";

private Directory directory;
@Inject private TagDAO tagDAO;
private int organizationId;

@Override
public void add(Tag tag) {
    IndexWriterConfig indexWriterConfig = new IndexWriterConfig(Version.LUCENE_35, new StandardAnalyzer(Version.LUCENE_35));        
    IndexWriter indexWriter = getIndexWriter(indexWriterConfig);

    //Create document
    Document document = new Document();
    document.add(new Field("id", String.valueOf(tag.getId()), Field.Store.YES, Field.Index.NOT_ANALYZED));
    document.add(new Field("title", tag.getTitle(), Field.Store.NO, Field.Index.ANALYZED));

    try {
        indexWriter.addDocument(document);
        indexWriter.close();
    } catch (CorruptIndexException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

@Override
public synchronized void setDirectory(int organizationId) throws IOException {
    this.organizationId = organizationId;
    File path = new File(INDEX_PATH + "/" + String.valueOf(organizationId));

    //If path does not exist, create it and create new index for organization
    if(!path.exists()) {
        path.mkdirs();
        buildCompleteIndex(organizationId, false);
    }

    this.directory = FSDirectory.open(path); //Open directory
}

private void buildCompleteIndex(int organizationId, boolean rebuildDir) {
    if(rebuildDir) {
        File path = new File(INDEX_PATH + "/" + String.valueOf(organizationId));
        try {
            Utils.deleteDirectory(path);
        } catch (IOException e) {
            throw new LuceneIndexException("Error rebuilding index directory.", e);
        }
        path.mkdirs();
    }

    List<Tag> tagList = tagDAO.findAll(organizationId);
    for(Tag tag : tagList) {
        add(tag);
    }
}

private IndexReader getIndexReader() {
    try {
        return IndexReader.open(directory);
    } catch (CorruptIndexException e) {
        buildCompleteIndex(organizationId, true);
    } catch (IOException e) {
        throw new LuceneIndexException("IOException prevented IndexReader from opening Index.", e);
    } catch(NullPointerException e) {
        throw new LuceneIndexException("Index resource not available.", e);
    }
    return null;
}

}

В некоторых ситуациях я ловлю исключение, если по какой-либо причине индекс становится поврежденным или просто еще не создан. В этом случае вызывается метод buildCompleteIndex (), который удаляет каталог и воссоздает индекс из источника данных.

В многопоточной среде, как я могу защитить от другого потока, создающего экземпляр класса и использующего метод, который вызывает каталог, когда он либо удаляется, либо перестраивается? Метод setDirectory () должен быть вызван до того, как сработает любой из других методов, поэтому я предполагаю, что установка синхронизации для метода решит эту проблему, но что, если каталог повреждается, когда потоки уже в нем, все они начинают вызывать пересборку? метод в то же время? Другими словами, я немного озадачен правильным способом удаления и обновления файлов ввода-вывода в многопоточной среде. Некоторые советы будут оценены.

Ответы [ 2 ]

0 голосов
/ 24 марта 2012

IndexWriter уже поточно-ориентирован, поэтому вы, если возможно, используете один и тот же экземпляр из разных потоков.

Если нет, рассмотрите возможность выполнения всех манипуляций с индексами в одном потоке. Это довольно легко сделать, используя java ExecutorService .

0 голосов
/ 23 марта 2012

Проверяя ваш код, вы синхронизировались по setDirectory (). Для всех потоков, которые используют этот конкретный экземпляр, он будет блокироваться до тех пор, пока этот метод не завершится. Это включает buildCompleteIndex. Если у вас есть потоки, которые создают новый экземпляр этого класса, вам нужно будет выполнить синхронизацию с чем-то другим. Например, вы можете синхронизировать статический экземпляр.

Синхронизация файлов и / или папок может быть очень сложной, особенно если вы работаете в нескольких средах (например, JDK).

Если я понимаю вашу проблему, может сработать что-то вроде следующего:

public class TestSearchEngine implements SearchEngine<Tag> {

private static final String INDEX_PATH = "/test/index";

private Directory directory;
@Inject private TagDAO tagDAO;
private int organizationId;

private static final Object mutex = new Object();

@Override
public void add(Tag tag) {
    IndexWriterConfig indexWriterConfig = new IndexWriterConfig(Version.LUCENE_35, new StandardAnalyzer(Version.LUCENE_35));        
    IndexWriter indexWriter = getIndexWriter(indexWriterConfig);

    //Create document
    Document document = new Document();
    document.add(new Field("id", String.valueOf(tag.getId()), Field.Store.YES, Field.Index.NOT_ANALYZED));
    document.add(new Field("title", tag.getTitle(), Field.Store.NO, Field.Index.ANALYZED));

    try {
        indexWriter.addDocument(document);
        indexWriter.close();
    } catch (CorruptIndexException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

@Override
public void setDirectory(int organizationId) throws IOException {

      synchronized (mutex) {
        this.organizationId = organizationId;
        File path = new File(INDEX_PATH + "/" + String.valueOf(organizationId));

        //If path does not exist, create it and create new index for organization
        if(!path.exists()) {
           path.mkdirs();
           buildCompleteIndex(organizationId, false);
        }

        this.directory = FSDirectory.open(path); //Open directory
      }
}

private void buildCompleteIndex(int organizationId, boolean rebuildDir) {
    if(rebuildDir) {
        File path = new File(INDEX_PATH + "/" + String.valueOf(organizationId));
        try {
            Utils.deleteDirectory(path);
        } catch (IOException e) {
            throw new LuceneIndexException("Error rebuilding index directory.", e);
        }
        path.mkdirs();
    }

    List<Tag> tagList = tagDAO.findAll(organizationId);
    for(Tag tag : tagList) {
        add(tag);
    }
}

private IndexReader getIndexReader() {
    try {
        return IndexReader.open(directory);
    } catch (CorruptIndexException e) {
        buildCompleteIndex(organizationId, true);
    } catch (IOException e) {
        throw new LuceneIndexException("IOException prevented IndexReader from opening Index.", e);
    } catch(NullPointerException e) {
        throw new LuceneIndexException("Index resource not available.", e);
    }
    return null;
}
...