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();
+ }
+ }
+}