Skip to content

Commit

Permalink
Open legacy indexes in read only mode in case database started in rea…
Browse files Browse the repository at this point in the history
…d  only mode.

Using lucene index in write mode can cause index corruption/complete deletion in cases if user code use/rely on interrupts.
To avoid such cases as much as possible indexes will be opened in read only mode in case if database was started in read only mode.
As result after interruption lucene will raise ClosedByInterruptException exception as soon as we will try to get something from index again, but index itself will be untouched.
  • Loading branch information
MishaDemianenko committed Jul 27, 2016
1 parent 229c37b commit 782b764
Show file tree
Hide file tree
Showing 14 changed files with 1,108 additions and 185 deletions.
Expand Up @@ -35,11 +35,11 @@ public void elementCleaned( IndexReference searcher )
{ {
try try
{ {
searcher.dispose( true ); searcher.dispose();
} }
catch ( IOException e ) catch ( IOException e )
{ {
throw new RuntimeException( e ); throw new RuntimeException( e );
} }
} }
} }
@@ -1,4 +1,4 @@
/* /*
* Copyright (c) 2002-2016 "Neo Technology," * Copyright (c) 2002-2016 "Neo Technology,"
* Network Engine for Objects in Lund AB [http://neotechnology.com] * Network Engine for Objects in Lund AB [http://neotechnology.com]
* *
Expand All @@ -19,21 +19,18 @@
*/ */
package org.neo4j.index.impl.lucene.legacy; package org.neo4j.index.impl.lucene.legacy;


import java.io.IOException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

import org.apache.lucene.index.IndexWriter; import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.IndexSearcher;


class IndexReference import java.io.IOException;
import java.util.concurrent.atomic.AtomicInteger;

abstract class IndexReference
{ {
private final IndexIdentifier identifier; private final IndexIdentifier identifier;
private final IndexWriter writer;
private final IndexSearcher searcher; private final IndexSearcher searcher;
private final AtomicInteger refCount = new AtomicInteger( 0 ); private final AtomicInteger refCount = new AtomicInteger( 0 );
private boolean searcherIsClosed; private boolean searcherIsClosed;
private boolean writerIsClosed;


/** /**
* We need this because we only want to close the reader/searcher if * We need this because we only want to close the reader/searcher if
Expand All @@ -43,23 +40,23 @@ class IndexReference
*/ */
private volatile boolean detached; private volatile boolean detached;


private final AtomicBoolean stale = new AtomicBoolean(); public IndexReference( IndexIdentifier identifier, IndexSearcher searcher )

public IndexReference( IndexIdentifier identifier, IndexSearcher searcher, IndexWriter writer )
{ {
this.identifier = identifier; this.identifier = identifier;
this.searcher = searcher; this.searcher = searcher;
this.writer = writer;
} }


public IndexSearcher getSearcher() abstract IndexWriter getWriter();
{
return this.searcher;
}


public IndexWriter getWriter() abstract void dispose() throws IOException;

abstract boolean checkAndClearStale();

abstract void setStale();

public IndexSearcher getSearcher()
{ {
return writer; return searcher;
} }


public IndexIdentifier getIdentifier() public IndexIdentifier getIdentifier()
Expand All @@ -72,34 +69,28 @@ void incRef()
this.refCount.incrementAndGet(); this.refCount.incrementAndGet();
} }


public synchronized void dispose( boolean writerAlso ) throws IOException void disposeSearcher() throws IOException
{ {
if ( !searcherIsClosed ) if ( !searcherIsClosed )
{ {
searcher.getIndexReader().close(); searcher.getIndexReader().close();
searcherIsClosed = true; searcherIsClosed = true;
} }

if ( writerAlso && !writerIsClosed )
{
writer.close();
writerIsClosed = true;
}
} }


public /*synchronized externally*/ void detachOrClose() throws IOException void detachOrClose() throws IOException
{ {
if ( this.refCount.get() == 0 ) if ( this.refCount.get() == 0 )
{ {
dispose( false ); disposeSearcher();
} }
else else
{ {
this.detached = true; this.detached = true;
} }
} }


synchronized boolean close() public synchronized boolean close()
{ {
try try
{ {
Expand All @@ -111,7 +102,7 @@ synchronized boolean close()
boolean reallyClosed = false; boolean reallyClosed = false;
if ( this.refCount.decrementAndGet() <= 0 && this.detached ) if ( this.refCount.decrementAndGet() <= 0 && this.detached )
{ {
dispose( false ); disposeSearcher();
reallyClosed = true; reallyClosed = true;
} }
return reallyClosed; return reallyClosed;
Expand All @@ -122,18 +113,13 @@ synchronized boolean close()
} }
} }


/*synchronized externally*/ boolean isClosed() public boolean isClosed()
{ {
return searcherIsClosed; return searcherIsClosed;
} }


/*synchronized externally*/ boolean checkAndClearStale() boolean isDetached()
{
return stale.compareAndSet( true, false );
}

public synchronized void setStale()
{ {
stale.set( true ); return detached;
} }
} }
@@ -0,0 +1,84 @@
/*
* Copyright (c) 2002-2016 "Neo Technology,"
* Network Engine for Objects in Lund AB [http://neotechnology.com]
*
* This file is part of Neo4j.
*
* Neo4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.neo4j.index.impl.lucene.legacy;

import org.apache.lucene.index.IndexReader;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.store.Directory;

import java.io.File;
import java.io.IOException;

/**
* Factory that build appropriate (read only or writable) {@link IndexReference} for provided {@link IndexIdentifier}
* or refresh previously constructed instance.
*/
abstract class IndexReferenceFactory
{
private final File baseStorePath;
private final IndexTypeCache typeCache;
private final LuceneDataSource.LuceneFilesystemFacade filesystemFacade;

IndexReferenceFactory( LuceneDataSource.LuceneFilesystemFacade filesystemFacade, File baseStorePath,
IndexTypeCache typeCache )
{
this.filesystemFacade = filesystemFacade;
this.baseStorePath = baseStorePath;
this.typeCache = typeCache;
}

/**
* Create new {@link IndexReference} for provided {@link IndexIdentifier}.
* @param indexIdentifier index identifier to build index for.
* @return newly create {@link IndexReference}
*
* @throws IOException in case of exception during accessing lucene reader/writer.
*/
abstract IndexReference createIndexReference( IndexIdentifier indexIdentifier ) throws IOException;

/**
* Refresh previously constructed indexReference.
* @param indexReference index reference to refresh
* @return refreshed index reference
*/
abstract IndexReference refresh( IndexReference indexReference );

Directory getIndexDirectory( IndexIdentifier identifier ) throws IOException
{
return filesystemFacade.getDirectory( baseStorePath, identifier );
}

IndexSearcher newIndexSearcher( IndexIdentifier identifier, IndexReader reader )
{
IndexSearcher searcher = new IndexSearcher( reader );
IndexType type = getType( identifier );
if ( type.getSimilarity() != null )
{
searcher.setSimilarity( type.getSimilarity() );
}
return searcher;
}

IndexType getType( IndexIdentifier identifier )
{
return typeCache.getIndexType( identifier, false );
}
}

0 comments on commit 782b764

Please sign in to comment.