diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt index a4bb3c0f3dc..973183b5b7e 100644 --- a/solr/CHANGES.txt +++ b/solr/CHANGES.txt @@ -760,6 +760,9 @@ Other Changes * SOLR-2712: expecting fl=score to return all fields is now deprecated. In solr 4.0, this will only return the score. (ryan) +* SOLR-3156: Check for Lucene directory locks at startup. In previous versions + this check was only performed during modifying (e.g. adding and deleting + documents) the index. (Luca Cavanna via Martijn van Groningen) Build ---------------------- diff --git a/solr/core/src/java/org/apache/solr/core/SolrCore.java b/solr/core/src/java/org/apache/solr/core/SolrCore.java index 6abdee69337..5a92ba7cd26 100644 --- a/solr/core/src/java/org/apache/solr/core/SolrCore.java +++ b/solr/core/src/java/org/apache/solr/core/SolrCore.java @@ -17,12 +17,14 @@ package org.apache.solr.core; +import org.apache.commons.io.IOUtils; import org.apache.lucene.codecs.Codec; -import org.apache.lucene.index.IndexDeletionPolicy; import org.apache.lucene.index.DirectoryReader; +import org.apache.lucene.index.IndexDeletionPolicy; import org.apache.lucene.index.IndexWriter; import org.apache.lucene.search.BooleanQuery; import org.apache.lucene.store.Directory; +import org.apache.lucene.store.LockObtainFailedException; import org.apache.solr.common.SolrException; import org.apache.solr.common.params.CommonParams; import org.apache.solr.common.params.CommonParams.EchoParamStyle; @@ -31,7 +33,8 @@ import org.apache.solr.common.util.NamedList; import org.apache.solr.common.util.SimpleOrderedMap; import org.apache.solr.handler.admin.ShowFileRequestHandler; import org.apache.solr.handler.component.*; -import org.apache.solr.request.*; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.request.SolrRequestHandler; import org.apache.solr.response.*; import org.apache.solr.response.transform.TransformerFactory; import org.apache.solr.schema.IndexSchema; @@ -45,23 +48,20 @@ import org.apache.solr.update.UpdateHandler; import org.apache.solr.update.processor.*; import org.apache.solr.util.RefCounted; import org.apache.solr.util.plugin.NamedListInitializedPlugin; -import org.apache.solr.util.plugin.SolrCoreAware; import org.apache.solr.util.plugin.PluginInfoInitialized; -import org.apache.commons.io.IOUtils; +import org.apache.solr.util.plugin.SolrCoreAware; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.xml.sax.SAXException; import javax.xml.parsers.ParserConfigurationException; - import java.io.*; +import java.lang.reflect.Constructor; +import java.net.URL; import java.util.*; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import java.net.URL; -import java.lang.reflect.Constructor; import java.util.concurrent.locks.ReentrantLock; @@ -366,14 +366,19 @@ public final class SolrCore implements SolrInfoMBean { initIndexReaderFactory(); - if (indexExists && firstTime && removeLocks) { + if (indexExists && firstTime) { // to remove locks, the directory must already exist... so we create it // if it didn't exist already... Directory dir = directoryFactory.get(indexDir, getSolrConfig().mainIndexConfig.lockType); if (dir != null) { if (IndexWriter.isLocked(dir)) { - log.warn(logid+"WARNING: Solr index directory '" + indexDir+ "' is locked. Unlocking..."); - IndexWriter.unlock(dir); + if (removeLocks) { + log.warn(logid + "WARNING: Solr index directory '{}' is locked. Unlocking...", indexDir); + IndexWriter.unlock(dir); + } else { + log.error(logid + "Solr index directory '{}' is locked. Throwing exception", indexDir); + throw new LockObtainFailedException("Index locked for write for core " + name); + } } directoryFactory.release(dir); } diff --git a/solr/core/src/test-files/solr/conf/solrconfig-nativelock.xml b/solr/core/src/test-files/solr/conf/solrconfig-nativelock.xml new file mode 100644 index 00000000000..0ad26f999ed --- /dev/null +++ b/solr/core/src/test-files/solr/conf/solrconfig-nativelock.xml @@ -0,0 +1,33 @@ + + + + + + + + ${solr.data.dir:} + + ${tests.luceneMatchVersion:LUCENE_CURRENT} + + + native + + diff --git a/solr/core/src/test-files/solr/conf/solrconfig-simplelock.xml b/solr/core/src/test-files/solr/conf/solrconfig-simplelock.xml new file mode 100644 index 00000000000..f46c7c2ac46 --- /dev/null +++ b/solr/core/src/test-files/solr/conf/solrconfig-simplelock.xml @@ -0,0 +1,33 @@ + + + + + + + + ${solr.data.dir:} + + ${tests.luceneMatchVersion:LUCENE_CURRENT} + + + simple + + diff --git a/solr/core/src/test/org/apache/solr/core/SolrCoreCheckLockOnStartupTest.java b/solr/core/src/test/org/apache/solr/core/SolrCoreCheckLockOnStartupTest.java new file mode 100644 index 00000000000..0ca79d064cd --- /dev/null +++ b/solr/core/src/test/org/apache/solr/core/SolrCoreCheckLockOnStartupTest.java @@ -0,0 +1,101 @@ +package org.apache.solr.core; + +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import org.apache.lucene.index.IndexWriter; +import org.apache.lucene.index.IndexWriterConfig; +import org.apache.lucene.store.Directory; +import org.apache.lucene.store.LockObtainFailedException; +import org.apache.lucene.store.NativeFSLockFactory; +import org.apache.lucene.store.SimpleFSLockFactory; +import org.apache.lucene.util.Version; +import org.apache.solr.SolrTestCaseJ4; +import org.junit.Before; +import org.junit.Test; + +import java.io.File; + +public class SolrCoreCheckLockOnStartupTest extends SolrTestCaseJ4 { + + @Before + public void setUp() throws Exception { + super.setUp(); + + System.setProperty("solr.directoryFactory", "org.apache.solr.core.SimpleFSDirectoryFactory"); + + //explicitly creates the temp dataDir so we know where the index will be located + createTempDir(); + + IndexWriterConfig indexWriterConfig = new IndexWriterConfig(Version.LUCENE_40, null); + Directory directory = newFSDirectory(new File(dataDir, "index")); + //creates a new index on the known location + new IndexWriter( + directory, + indexWriterConfig.setOpenMode(IndexWriterConfig.OpenMode.CREATE) + ).close(); + directory.close(); + } + + @Test + public void testSimpleLockErrorOnStartup() throws Exception { + + Directory directory = newFSDirectory(new File(dataDir, "index"), new SimpleFSLockFactory()); + //creates a new IndexWriter without releasing the lock yet + IndexWriter indexWriter = new IndexWriter(directory, new IndexWriterConfig(Version.LUCENE_40, null)); + + try { + //opening a new core on the same index + initCore("solrconfig-simplelock.xml", "schema.xml"); + fail("Expected " + LockObtainFailedException.class.getSimpleName()); + } catch (Throwable t) { + assertTrue(t instanceof RuntimeException); + assertNotNull(t.getCause()); + assertTrue(t.getCause() instanceof RuntimeException); + assertNotNull(t.getCause().getCause()); + assertTrue(t.getCause().getCause() instanceof LockObtainFailedException); + } finally { + indexWriter.close(); + directory.close(); + deleteCore(); + } + } + + @Test + public void testNativeLockErrorOnStartup() throws Exception { + + Directory directory = newFSDirectory(new File(dataDir, "index"), new NativeFSLockFactory()); + //creates a new IndexWriter without releasing the lock yet + IndexWriter indexWriter = new IndexWriter(directory, new IndexWriterConfig(Version.LUCENE_40, null)); + + try { + //opening a new core on the same index + initCore("solrconfig-nativelock.xml", "schema.xml"); + fail("Expected " + LockObtainFailedException.class.getSimpleName()); + } catch(Throwable t) { + assertTrue(t instanceof RuntimeException); + assertNotNull(t.getCause()); + assertTrue(t.getCause() instanceof RuntimeException); + assertNotNull(t.getCause().getCause()); + assertTrue(t.getCause().getCause() instanceof LockObtainFailedException); + } finally { + indexWriter.close(); + directory.close(); + deleteCore(); + } + } +}