Core: separately log file deletions

Today, you can turn on lucene.iw TRACE logging, but that produces tons
of output.  This changes breaks out separate lucene.iw.ifd and
index.store.deletes logger components (TRACE), disabled by default, to
see what part of Elasticsearch is deleting index files.

Closed #8662

Closed #8603
This commit is contained in:
Michael McCandless 2014-11-26 05:10:47 -05:00 committed by mikemccand
parent 099b1a70d5
commit d9dfad0e9b
6 changed files with 96 additions and 21 deletions

View File

@ -26,23 +26,38 @@ import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.index.shard.ShardId;
/** An InfoStream (for Lucene's IndexWriter) that redirects /** An InfoStream (for Lucene's IndexWriter) that redirects
* messages to Logger.trace. */ * messages to "lucene.iw.ifd" and "lucene.iw" Logger.trace. */
public final class LoggerInfoStream extends InfoStream { public final class LoggerInfoStream extends InfoStream {
/** Used for component-specific logging: */
/** Logger for everything */
private final ESLogger logger; private final ESLogger logger;
/** Logger for IndexFileDeleter */
private final ESLogger ifdLogger;
public LoggerInfoStream(Settings settings, ShardId shardId) { public LoggerInfoStream(Settings settings, ShardId shardId) {
logger = Loggers.getLogger("lucene.iw", settings, shardId); logger = Loggers.getLogger("lucene.iw", settings, shardId);
ifdLogger = Loggers.getLogger("lucene.iw.ifd", settings, shardId);
} }
public void message(String component, String message) { public void message(String component, String message) {
logger.trace("{} {}: {}", Thread.currentThread().getName(), component, message); getLogger(component).trace("{} {}: {}", Thread.currentThread().getName(), component, message);
} }
public boolean isEnabled(String component) { public boolean isEnabled(String component) {
// TP is a special "test point" component; we don't want // TP is a special "test point" component; we don't want
// to log it: // to log it:
return logger.isTraceEnabled() && component.equals("TP") == false; return getLogger(component).isTraceEnabled() && component.equals("TP") == false;
}
private ESLogger getLogger(String component) {
if (component.equals("IFD")) {
return ifdLogger;
} else {
return logger;
}
} }
@Override @Override

View File

@ -806,6 +806,7 @@ public class BlobStoreIndexShardRepository extends AbstractComponent implements
for (String storeFile : store.directory().listAll()) { for (String storeFile : store.directory().listAll()) {
if (!Store.isChecksum(storeFile) && !snapshot.containPhysicalIndexFile(storeFile)) { if (!Store.isChecksum(storeFile) && !snapshot.containPhysicalIndexFile(storeFile)) {
try { try {
store.logDeleteFile("restore", storeFile);
store.directory().deleteFile(storeFile); store.directory().deleteFile(storeFile);
} catch (IOException e) { } catch (IOException e) {
// ignore // ignore

View File

@ -537,8 +537,11 @@ public class Store extends AbstractIndexShardComponent implements Closeable, Ref
*/ */
public final class StoreDirectory extends FilterDirectory { public final class StoreDirectory extends FilterDirectory {
public final ESLogger deletesLogger;
StoreDirectory(Directory delegateDirectory) throws IOException { StoreDirectory(Directory delegateDirectory) throws IOException {
super(delegateDirectory); super(delegateDirectory);
deletesLogger = Loggers.getLogger("index.store.deletes", indexSettings, shardId);
} }
public ShardId shardId() { public ShardId shardId() {
@ -551,6 +554,12 @@ public class Store extends AbstractIndexShardComponent implements Closeable, Ref
assert false : "Nobody should close this directory except of the Store itself"; assert false : "Nobody should close this directory except of the Store itself";
} }
@Override
public void deleteFile(String name) throws IOException {
logDeleteFile("StoreDirectory.deleteFile", name);
super.deleteFile(name);
}
private void innerClose() throws IOException { private void innerClose() throws IOException {
super.close(); super.close();
} }
@ -561,8 +570,22 @@ public class Store extends AbstractIndexShardComponent implements Closeable, Ref
} }
} }
/** Log that we are about to delete this file, to the index.store.deletes component. */
public void logDeleteFile(String message, String fileName) {
logDeleteFile(directory(), message, fileName);
}
/** Log that we are about to delete this file, to the index.store.deletes component. */
public static void logDeleteFile(Directory dir, String message, String fileName) {
assert dir instanceof StoreDirectory;
if (dir instanceof StoreDirectory) {
((StoreDirectory) dir).deletesLogger.trace("{}: delete file {}", message, fileName);
}
// else what to do...?
}
/** /**
* Represents a snaphshot of the current directory build from the latest Lucene commit. * Represents a snapshot of the current directory build from the latest Lucene commit.
* Only files that are part of the last commit are considered in this datastrucutre. * Only files that are part of the last commit are considered in this datastrucutre.
* For backwards compatibility the snapshot might include legacy checksums that * For backwards compatibility the snapshot might include legacy checksums that
* are derived from a dedicated checksum file written by older elasticsearch version pre 1.3 * are derived from a dedicated checksum file written by older elasticsearch version pre 1.3

View File

@ -385,6 +385,7 @@ public class RecoveryTarget extends AbstractComponent {
// don't delete snapshot file, or the checksums file (note, this is extra protection since the Store won't delete checksum) // don't delete snapshot file, or the checksums file (note, this is extra protection since the Store won't delete checksum)
if (!request.snapshotFiles().contains(existingFile) && !Store.isChecksum(existingFile)) { if (!request.snapshotFiles().contains(existingFile) && !Store.isChecksum(existingFile)) {
try { try {
store.logDeleteFile("recovery CleanFilesRequestHandler", existingFile);
store.directory().deleteFile(existingFile); store.directory().deleteFile(existingFile);
} catch (Exception e) { } catch (Exception e) {
// ignore, we don't really care, will get deleted later on // ignore, we don't really care, will get deleted later on

View File

@ -57,6 +57,8 @@ public class InternalEngineIntegrationTest extends ElasticsearchIntegrationTest
private void assertTotalCompoundSegments(int i, int t, String index) { private void assertTotalCompoundSegments(int i, int t, String index) {
IndicesSegmentResponse indicesSegmentResponse = client().admin().indices().prepareSegments(index).get(); IndicesSegmentResponse indicesSegmentResponse = client().admin().indices().prepareSegments(index).get();
IndexSegments indexSegments = indicesSegmentResponse.getIndices().get(index); IndexSegments indexSegments = indicesSegmentResponse.getIndices().get(index);
assertNotNull(indexSegments);
assertNotNull(indexSegments.getShards());
Collection<IndexShardSegments> values = indexSegments.getShards().values(); Collection<IndexShardSegments> values = indexSegments.getShards().values();
int compounds = 0; int compounds = 0;
int total = 0; int total = 0;

View File

@ -1253,27 +1253,30 @@ public class InternalEngineTests extends ElasticsearchTestCase {
} }
private static class MockAppender extends AppenderSkeleton { private static class MockAppender extends AppenderSkeleton {
public boolean sawIndexWriterMessage; public boolean sawIndexWriterMessage;
public boolean sawIndexWriterIFDMessage;
@Override @Override
protected void append(LoggingEvent event) { protected void append(LoggingEvent event) {
if (event.getLevel() == Level.TRACE && if (event.getLevel() == Level.TRACE && event.getMessage().toString().contains("[index][1] ")) {
event.getLoggerName().endsWith("lucene.iw") && if (event.getLoggerName().endsWith("lucene.iw") &&
event.getMessage().toString().contains("IW: apply all deletes during flush") && event.getMessage().toString().contains("IW: apply all deletes during flush")) {
event.getMessage().toString().contains("[index][1] ")) { sawIndexWriterMessage = true;
}
sawIndexWriterMessage = true; if (event.getLoggerName().endsWith("lucene.iw.ifd")) {
sawIndexWriterIFDMessage = true;
}
}
} }
}
@Override @Override
public boolean requiresLayout() { public boolean requiresLayout() {
return false; return false;
} }
@Override @Override
public void close() { public void close() {
} }
} }
// #5891: make sure IndexWriter's infoStream output is // #5891: make sure IndexWriter's infoStream output is
@ -1307,6 +1310,36 @@ public class InternalEngineTests extends ElasticsearchTestCase {
} }
} }
// #8603: make sure we can separately log IFD's messages
public void testIndexWriterIFDInfoStream() {
MockAppender mockAppender = new MockAppender();
Logger iwIFDLogger = Logger.getLogger("lucene.iw.ifd");
Level savedLevel = iwIFDLogger.getLevel();
iwIFDLogger.addAppender(mockAppender);
iwIFDLogger.setLevel(Level.DEBUG);
try {
// First, with DEBUG, which should NOT log IndexWriter output:
ParsedDocument doc = testParsedDocument("1", "1", "test", null, -1, -1, testDocumentWithTextField(), Lucene.STANDARD_ANALYZER, B_1, false);
engine.create(new Engine.Create(null, newUid("1"), doc));
engine.flush(new Engine.Flush());
assertFalse(mockAppender.sawIndexWriterMessage);
assertFalse(mockAppender.sawIndexWriterIFDMessage);
// Again, with TRACE, which should only log IndexWriter IFD output:
iwIFDLogger.setLevel(Level.TRACE);
engine.create(new Engine.Create(null, newUid("2"), doc));
engine.flush(new Engine.Flush());
assertFalse(mockAppender.sawIndexWriterMessage);
assertTrue(mockAppender.sawIndexWriterIFDMessage);
} finally {
iwIFDLogger.removeAppender(mockAppender);
iwIFDLogger.setLevel(null);
}
}
@Slow @Slow
@Test @Test
public void testEnableGcDeletes() throws Exception { public void testEnableGcDeletes() throws Exception {