mirror of https://github.com/apache/lucene.git
SOLR-1953: It may be possible for temporary files to accumulate until the Solr process is shut down.
This commit is contained in:
parent
22d9af41a4
commit
e82399d067
|
@ -244,6 +244,9 @@ Bug Fixes
|
|||
|
||||
* SOLR-9823: CoreContainer incorrectly setting MDCLoggingContext for core (Jessica Cheng Mallet via Erick Erickson)
|
||||
|
||||
* SOLR-1953: It may be possible for temporary files to accumulate until the Solr process is shut down.
|
||||
(Karl Wright, Mark Miller)
|
||||
|
||||
Other Changes
|
||||
----------------------
|
||||
|
||||
|
|
|
@ -45,6 +45,7 @@ import java.util.concurrent.atomic.AtomicReference;
|
|||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.commons.io.FileCleaningTracker;
|
||||
import org.apache.commons.io.input.CloseShieldInputStream;
|
||||
import org.apache.commons.io.output.CloseShieldOutputStream;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
@ -62,6 +63,7 @@ import org.apache.solr.core.SolrXmlConfig;
|
|||
import org.apache.solr.request.SolrRequestInfo;
|
||||
import org.apache.solr.security.AuthenticationPlugin;
|
||||
import org.apache.solr.security.PKIAuthenticationPlugin;
|
||||
import org.apache.solr.util.SolrFileCleaningTracker;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
@ -123,6 +125,8 @@ public class SolrDispatchFilter extends BaseSolrFilter {
|
|||
{
|
||||
log.trace("SolrDispatchFilter.init(): {}", this.getClass().getClassLoader());
|
||||
|
||||
SolrRequestParsers.fileCleaningTracker = new SolrFileCleaningTracker();
|
||||
|
||||
StartupLoggingUtils.checkLogDir();
|
||||
logWelcomeBanner();
|
||||
String muteConsole = System.getProperty(SOLR_LOG_MUTECONSOLE);
|
||||
|
@ -240,6 +244,17 @@ public class SolrDispatchFilter extends BaseSolrFilter {
|
|||
|
||||
@Override
|
||||
public void destroy() {
|
||||
try {
|
||||
FileCleaningTracker fileCleaningTracker = SolrRequestParsers.fileCleaningTracker;
|
||||
if (fileCleaningTracker != null) {
|
||||
fileCleaningTracker.exitWhenFinished();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("Exception closing FileCleaningTracker", e);
|
||||
} finally {
|
||||
SolrRequestParsers.fileCleaningTracker = null;
|
||||
}
|
||||
|
||||
if (cores != null) {
|
||||
try {
|
||||
cores.shutdown();
|
||||
|
|
|
@ -42,6 +42,7 @@ import java.util.Map;
|
|||
import org.apache.commons.fileupload.FileItem;
|
||||
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
|
||||
import org.apache.commons.fileupload.servlet.ServletFileUpload;
|
||||
import org.apache.commons.io.FileCleaningTracker;
|
||||
import org.apache.commons.io.input.CloseShieldInputStream;
|
||||
import org.apache.lucene.util.IOUtils;
|
||||
import org.apache.solr.common.SolrException;
|
||||
|
@ -58,6 +59,7 @@ import org.apache.solr.core.SolrCore;
|
|||
import org.apache.solr.request.SolrQueryRequest;
|
||||
import org.apache.solr.request.SolrQueryRequestBase;
|
||||
import org.apache.solr.util.RTimerTree;
|
||||
import org.apache.solr.util.SolrFileCleaningTracker;
|
||||
|
||||
import static org.apache.solr.common.params.CommonParams.PATH;
|
||||
|
||||
|
@ -88,6 +90,8 @@ public class SolrRequestParsers
|
|||
/** Default instance for e.g. admin requests. Limits to 2 MB uploads and does not allow remote streams. */
|
||||
public static final SolrRequestParsers DEFAULT = new SolrRequestParsers();
|
||||
|
||||
public static volatile SolrFileCleaningTracker fileCleaningTracker;
|
||||
|
||||
/**
|
||||
* Pass in an xml configuration. A null configuration will enable
|
||||
* everything with maximum values.
|
||||
|
@ -532,31 +536,30 @@ public class SolrRequestParsers
|
|||
/**
|
||||
* Extract Multipart streams
|
||||
*/
|
||||
static class MultipartRequestParser implements SolrRequestParser
|
||||
{
|
||||
static class MultipartRequestParser implements SolrRequestParser {
|
||||
private final int uploadLimitKB;
|
||||
private DiskFileItemFactory factory = new DiskFileItemFactory();
|
||||
|
||||
public MultipartRequestParser( int limit )
|
||||
{
|
||||
public MultipartRequestParser(int limit) {
|
||||
uploadLimitKB = limit;
|
||||
|
||||
// Set factory constraints
|
||||
FileCleaningTracker fct = fileCleaningTracker;
|
||||
if (fct != null) {
|
||||
factory.setFileCleaningTracker(fileCleaningTracker);
|
||||
}
|
||||
// TODO - configure factory.setSizeThreshold(yourMaxMemorySize);
|
||||
// TODO - configure factory.setRepository(yourTempDirectory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SolrParams parseParamsAndFillStreams(
|
||||
final HttpServletRequest req, ArrayList<ContentStream> streams ) throws Exception
|
||||
{
|
||||
public SolrParams parseParamsAndFillStreams(
|
||||
final HttpServletRequest req, ArrayList<ContentStream> streams) throws Exception {
|
||||
if( !ServletFileUpload.isMultipartContent(req) ) {
|
||||
throw new SolrException( ErrorCode.BAD_REQUEST, "Not multipart content! "+req.getContentType() );
|
||||
}
|
||||
|
||||
MultiMapSolrParams params = parseQueryString( req.getQueryString() );
|
||||
|
||||
// Create a factory for disk-based file items
|
||||
DiskFileItemFactory factory = new DiskFileItemFactory();
|
||||
|
||||
// Set factory constraints
|
||||
// TODO - configure factory.setSizeThreshold(yourMaxMemorySize);
|
||||
// TODO - configure factory.setRepository(yourTempDirectory);
|
||||
|
||||
// Create a new file upload handler
|
||||
ServletFileUpload upload = new ServletFileUpload(factory);
|
||||
|
|
|
@ -0,0 +1,147 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.solr.util;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.ref.PhantomReference;
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.io.FileCleaningTracker;
|
||||
import org.apache.commons.io.FileDeleteStrategy;
|
||||
|
||||
public class SolrFileCleaningTracker extends FileCleaningTracker {
|
||||
|
||||
ReferenceQueue<Object> q = new ReferenceQueue<>();
|
||||
|
||||
final Collection<Tracker> trackers = Collections.synchronizedSet(new HashSet<Tracker>());
|
||||
|
||||
final List<String> deleteFailures = Collections.synchronizedList(new ArrayList<String>());
|
||||
|
||||
volatile boolean exitWhenFinished = false;
|
||||
|
||||
Thread reaper;
|
||||
|
||||
public void track(final File file, final Object marker) {
|
||||
track(file, marker, null);
|
||||
}
|
||||
|
||||
public void track(final File file, final Object marker, final FileDeleteStrategy deleteStrategy) {
|
||||
if (file == null) {
|
||||
throw new NullPointerException("The file must not be null");
|
||||
}
|
||||
addTracker(file.getPath(), marker, deleteStrategy);
|
||||
}
|
||||
|
||||
public void track(final String path, final Object marker) {
|
||||
track(path, marker, null);
|
||||
}
|
||||
|
||||
public void track(final String path, final Object marker, final FileDeleteStrategy deleteStrategy) {
|
||||
if (path == null) {
|
||||
throw new NullPointerException("The path must not be null");
|
||||
}
|
||||
addTracker(path, marker, deleteStrategy);
|
||||
}
|
||||
|
||||
private synchronized void addTracker(final String path, final Object marker,
|
||||
final FileDeleteStrategy deleteStrategy) {
|
||||
if (exitWhenFinished) {
|
||||
throw new IllegalStateException("No new trackers can be added once exitWhenFinished() is called");
|
||||
}
|
||||
if (reaper == null) {
|
||||
reaper = new Reaper();
|
||||
reaper.start();
|
||||
}
|
||||
trackers.add(new Tracker(path, deleteStrategy, marker, q));
|
||||
}
|
||||
|
||||
public int getTrackCount() {
|
||||
return trackers.size();
|
||||
}
|
||||
|
||||
public List<String> getDeleteFailures() {
|
||||
return deleteFailures;
|
||||
}
|
||||
|
||||
public synchronized void exitWhenFinished() {
|
||||
// synchronized block protects reaper
|
||||
exitWhenFinished = true;
|
||||
if (reaper != null) {
|
||||
synchronized (reaper) {
|
||||
reaper.interrupt();
|
||||
try {
|
||||
reaper.join();
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class Reaper extends Thread {
|
||||
Reaper() {
|
||||
super("MultiPart Upload Tmp File Reaper");
|
||||
setDaemon(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (exitWhenFinished == false || trackers.size() > 0) {
|
||||
try {
|
||||
// Wait for a tracker to remove.
|
||||
final Tracker tracker = (Tracker) q.remove(); // cannot return null
|
||||
trackers.remove(tracker);
|
||||
if (!tracker.delete()) {
|
||||
deleteFailures.add(tracker.getPath());
|
||||
}
|
||||
tracker.clear();
|
||||
} catch (final InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final class Tracker extends PhantomReference<Object> {
|
||||
|
||||
private final String path;
|
||||
|
||||
private final FileDeleteStrategy deleteStrategy;
|
||||
|
||||
Tracker(final String path, final FileDeleteStrategy deleteStrategy, final Object marker,
|
||||
final ReferenceQueue<? super Object> queue) {
|
||||
super(marker, queue);
|
||||
this.path = path;
|
||||
this.deleteStrategy = deleteStrategy == null ? FileDeleteStrategy.NORMAL : deleteStrategy;
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public boolean delete() {
|
||||
return deleteStrategy.deleteQuietly(new File(path));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue