blob: a4a013c3c186f2cfba53006dc41c4dbedd3d2d4e [file] [log] [blame]
Index: lucene/CHANGES.txt
===================================================================
--- lucene/CHANGES.txt (revision 1611404)
+++ lucene/CHANGES.txt (working copy)
@@ -111,6 +111,8 @@
* LUCENE-5826: Support proper hunspell case handling, LANG, KEEPCASE, NEEDAFFIX,
and ONLYINCOMPOUND flags. (Robert Muir)
+
+* LUCENE-5813: Directory implements Accountable. (Adrien Grand)
API Changes
Index: lucene/core/src/java/org/apache/lucene/store/CompoundFileDirectory.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/store/CompoundFileDirectory.java (revision 1611404)
+++ lucene/core/src/java/org/apache/lucene/store/CompoundFileDirectory.java (working copy)
@@ -259,6 +259,11 @@
}
@Override
+ public long ramBytesUsed() {
+ return directory.ramBytesUsed();
+ }
+
+ @Override
public String toString() {
return "CompoundFileDirectory(file=\"" + fileName + "\" in dir=" + directory + ")";
}
Index: lucene/core/src/java/org/apache/lucene/store/Directory.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/store/Directory.java (revision 1611404)
+++ lucene/core/src/java/org/apache/lucene/store/Directory.java (working copy)
@@ -23,6 +23,7 @@
import java.nio.file.NoSuchFileException;
import java.util.Collection; // for javadocs
+import org.apache.lucene.util.Accountable;
import org.apache.lucene.util.IOUtils;
/** A Directory is a flat list of files. Files may be written once, when they
@@ -41,7 +42,7 @@
* instance using {@link #setLockFactory}.
*
*/
-public abstract class Directory implements Closeable {
+public abstract class Directory implements Accountable, Closeable {
/**
* Returns an array of strings, one for each file in the directory.
Index: lucene/core/src/java/org/apache/lucene/store/FileSwitchDirectory.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/store/FileSwitchDirectory.java (revision 1611404)
+++ lucene/core/src/java/org/apache/lucene/store/FileSwitchDirectory.java (working copy)
@@ -166,4 +166,9 @@
public IndexInput openInput(String name, IOContext context) throws IOException {
return getDirectory(name).openInput(name, context);
}
+
+ @Override
+ public long ramBytesUsed() {
+ return primaryDir.ramBytesUsed() + secondaryDir.ramBytesUsed();
+ }
}
Index: lucene/core/src/java/org/apache/lucene/store/FilterDirectory.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/store/FilterDirectory.java (revision 1611404)
+++ lucene/core/src/java/org/apache/lucene/store/FilterDirectory.java (working copy)
@@ -110,4 +110,8 @@
return getClass().getSimpleName() + "(" + in.toString() + ")";
}
+ @Override
+ public long ramBytesUsed() {
+ return in.ramBytesUsed();
+ }
}
Index: lucene/core/src/java/org/apache/lucene/store/MMapDirectory.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/store/MMapDirectory.java (revision 1611404)
+++ lucene/core/src/java/org/apache/lucene/store/MMapDirectory.java (working copy)
@@ -285,4 +285,9 @@
}
}
};
+
+ @Override
+ public long ramBytesUsed() {
+ return 0;
+ }
}
Index: lucene/core/src/java/org/apache/lucene/store/NIOFSDirectory.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/store/NIOFSDirectory.java (revision 1611404)
+++ lucene/core/src/java/org/apache/lucene/store/NIOFSDirectory.java (working copy)
@@ -193,4 +193,9 @@
@Override
protected void seekInternal(long pos) throws IOException {}
}
+
+ @Override
+ public long ramBytesUsed() {
+ return 0;
+ }
}
Index: lucene/core/src/java/org/apache/lucene/store/NRTCachingDirectory.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/store/NRTCachingDirectory.java (revision 1611404)
+++ lucene/core/src/java/org/apache/lucene/store/NRTCachingDirectory.java (working copy)
@@ -64,7 +64,7 @@
* @lucene.experimental
*/
-public class NRTCachingDirectory extends FilterDirectory implements Accountable {
+public class NRTCachingDirectory extends FilterDirectory {
private final RAMDirectory cache = new RAMDirectory();
@@ -259,6 +259,6 @@
@Override
public long ramBytesUsed() {
- return cache.ramBytesUsed();
+ return super.ramBytesUsed() + cache.ramBytesUsed();
}
}
Index: lucene/core/src/java/org/apache/lucene/store/RAMDirectory.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/store/RAMDirectory.java (revision 1611404)
+++ lucene/core/src/java/org/apache/lucene/store/RAMDirectory.java (working copy)
@@ -47,7 +47,7 @@
* implementation working directly on the file system cache of the
* operating system, so copying data to Java heap space is not useful.
*/
-public class RAMDirectory extends BaseDirectory implements Accountable {
+public class RAMDirectory extends BaseDirectory {
protected final Map<String,RAMFile> fileMap = new ConcurrentHashMap<>();
protected final AtomicLong sizeInBytes = new AtomicLong();
Index: lucene/core/src/java/org/apache/lucene/store/RAMFile.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/store/RAMFile.java (revision 1611404)
+++ lucene/core/src/java/org/apache/lucene/store/RAMFile.java (working copy)
@@ -20,11 +20,15 @@
import java.util.ArrayList;
import org.apache.lucene.util.Accountable;
+import org.apache.lucene.util.RamUsageEstimator;
/**
* Represents a file in RAM as a list of byte[] buffers.
* @lucene.internal */
public class RAMFile implements Accountable {
+
+ private static final long BASE_RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance(RAMFile.class);
+
protected ArrayList<byte[]> buffers = new ArrayList<>();
long length;
RAMDirectory directory;
@@ -31,10 +35,14 @@
protected long sizeInBytes;
// File used as buffer, in no RAMDirectory
- public RAMFile() {}
+ public RAMFile() {
+ sizeInBytes = BASE_RAM_BYTES_USED;
+ }
RAMFile(RAMDirectory directory) {
+ this();
this.directory = directory;
+ directory.sizeInBytes.getAndAdd(sizeInBytes);
}
// For non-stream access from thread that might be concurrent with writing
@@ -48,13 +56,14 @@
protected final byte[] addBuffer(int size) {
byte[] buffer = newBuffer(size);
+ final long ramBytesUsed = RamUsageEstimator.NUM_BYTES_OBJECT_REF + RamUsageEstimator.sizeOf(buffer);
synchronized(this) {
buffers.add(buffer);
- sizeInBytes += size;
+ sizeInBytes += ramBytesUsed;
}
if (directory != null) {
- directory.sizeInBytes.getAndAdd(size);
+ directory.sizeInBytes.getAndAdd(ramBytesUsed);
}
return buffer;
}
Index: lucene/core/src/java/org/apache/lucene/store/SimpleFSDirectory.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/store/SimpleFSDirectory.java (revision 1611404)
+++ lucene/core/src/java/org/apache/lucene/store/SimpleFSDirectory.java (working copy)
@@ -159,4 +159,9 @@
return file.getFD().valid();
}
}
+
+ @Override
+ public long ramBytesUsed() {
+ return 0;
+ }
}
Index: lucene/core/src/test/org/apache/lucene/index/TestFieldsReader.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/index/TestFieldsReader.java (revision 1611404)
+++ lucene/core/src/test/org/apache/lucene/index/TestFieldsReader.java (working copy)
@@ -140,6 +140,10 @@
public void close() throws IOException {
fsDir.close();
}
+ @Override
+ public long ramBytesUsed() {
+ return fsDir.ramBytesUsed();
+ }
}
private static class FaultyIndexInput extends BufferedIndexInput {
Index: lucene/core/src/test/org/apache/lucene/store/TestBufferedIndexInput.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/store/TestBufferedIndexInput.java (revision 1611404)
+++ lucene/core/src/test/org/apache/lucene/store/TestBufferedIndexInput.java (working copy)
@@ -297,6 +297,11 @@
dir = new SimpleFSDirectory(path, null);
}
+ @Override
+ public long ramBytesUsed() {
+ return dir.ramBytesUsed();
+ }
+
public void tweakBufferSizes() {
//int count = 0;
for (final IndexInput ip : allIndexInputs) {
Index: lucene/misc/src/java/org/apache/lucene/store/NativeUnixDirectory.java
===================================================================
--- lucene/misc/src/java/org/apache/lucene/store/NativeUnixDirectory.java (revision 1611404)
+++ lucene/misc/src/java/org/apache/lucene/store/NativeUnixDirectory.java (working copy)
@@ -120,6 +120,11 @@
}
@Override
+ public long ramBytesUsed() {
+ return delegate.ramBytesUsed();
+ }
+
+ @Override
public IndexInput openInput(String name, IOContext context) throws IOException {
ensureOpen();
if (context.context != Context.MERGE || context.mergeInfo.estimatedMergeBytes < minBytesDirect || fileLength(name) < minBytesDirect) {
Index: lucene/misc/src/java/org/apache/lucene/store/WindowsDirectory.java
===================================================================
--- lucene/misc/src/java/org/apache/lucene/store/WindowsDirectory.java (revision 1611404)
+++ lucene/misc/src/java/org/apache/lucene/store/WindowsDirectory.java (working copy)
@@ -70,6 +70,11 @@
}
@Override
+ public long ramBytesUsed() {
+ return 0;
+ }
+
+ @Override
public IndexInput openInput(String name, IOContext context) throws IOException {
ensureOpen();
return new WindowsIndexInput(new File(getDirectory(), name), Math.max(BufferedIndexInput.bufferSize(context), DEFAULT_BUFFERSIZE));
Index: lucene/test-framework/src/java/org/apache/lucene/store/BaseDirectoryTestCase.java
===================================================================
--- lucene/test-framework/src/java/org/apache/lucene/store/BaseDirectoryTestCase.java (revision 1611404)
+++ lucene/test-framework/src/java/org/apache/lucene/store/BaseDirectoryTestCase.java (working copy)
@@ -21,11 +21,17 @@
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
+import java.lang.reflect.Field;
import java.nio.file.NoSuchFileException;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.zip.CRC32;
import org.apache.lucene.index.DirectoryReader;
@@ -32,6 +38,7 @@
import org.apache.lucene.index.IndexNotFoundException;
import org.apache.lucene.util.IOUtils;
import org.apache.lucene.util.LuceneTestCase;
+import org.apache.lucene.util.RamUsageTester;
import org.apache.lucene.util.TestUtil;
/** Base class for per-Directory tests. */
@@ -914,5 +921,94 @@
input.close();
dir.close();
}
+
+ private static void createRandomFile(Directory dir, String name, long size) throws IOException {
+ try (final IndexOutput out = dir.createOutput(name, IOContext.DEFAULT)) {
+ final byte[] bytes = new byte[1024];
+ for (long written = 0; written < size; ) {
+ random().nextBytes(bytes);
+ final int toWrite = (int) Math.min(bytes.length, size - written);
+ out.writeBytes(bytes, 0, toWrite);
+ written += toWrite;
+ }
+ }
+ }
+
+ private static final Field FS_DIRECTORY_STALE_FILES;
+ static {
+ try {
+ FS_DIRECTORY_STALE_FILES = FSDirectory.class.getDeclaredField("staleFiles");
+ } catch (NoSuchFieldException | SecurityException e) {
+ throw new Error(e);
+ }
+ }
+ private static final RamUsageTester.Accumulator DIRECTORY_ACCUMULATOR = new RamUsageTester.Accumulator() {
+
+ public long accumulateObject(Object o, long shallowSize, Map<Field, Object> fieldValues, Collection<Object> queue) {
+ if (o instanceof MockDirectoryWrapper) {
+ // MockDirectoryWrapper keeps lots of references on open inputs, not sync'd files, etc.
+ // just take the wrapped directory into account
+ final Map<Field, Object> newFieldValues = new HashMap<>();
+ for (Map.Entry<Field, Object> entry : fieldValues.entrySet()) {
+ if (entry.getValue() instanceof Directory) {
+ newFieldValues.put(entry.getKey(), entry.getValue());
+ }
+ }
+ fieldValues = newFieldValues;
+ }
+ if (o instanceof FSDirectory) {
+ fieldValues = new HashMap<>(fieldValues);
+ fieldValues.remove(FS_DIRECTORY_STALE_FILES);
+ }
+ return super.accumulateObject(o, shallowSize, fieldValues, queue);
+ }
+
+ };
+
+ public void testRamBytesUsed() throws IOException {
+ Directory dir = getDirectory(createTempDir("testBytes"));
+
+ // Create one large file and lots of small files
+ List<String> files = new ArrayList<>();
+ createRandomFile(dir, "large.bin", TestUtil.nextInt(random(), 1 << 18, 1 << 20));
+ files.add("large.bin");
+ final int numSmallFiles = TestUtil.nextInt(random(), 10, 1000);
+ for (int i = 0; i < numSmallFiles; ++i) {
+ final String fileName = "small" + i + ".bin";
+ createRandomFile(dir, fileName, TestUtil.nextInt(random(), 10, 1024));
+ files.add(fileName);
+ }
+ if (random().nextBoolean()) {
+ dir.sync(files);
+ }
+
+ Set<IndexInput> openInputs = new HashSet<>();
+ if (random().nextBoolean()) {
+ openInputs.add(dir.openInput("large.bin", IOContext.READ));
+ }
+ for (int i = 0; i < numSmallFiles; ++i) {
+ if (random().nextBoolean()) {
+ final String fileName = "small" + i + ".bin";
+ openInputs.add(dir.openInput(fileName, IOContext.READ));
+ }
+ }
+ for (IndexInput input : openInputs) {
+ if (input.length() >= 2) {
+ input.seek(random().nextInt((int) input.length() - 1));
+ input.readByte();
+ }
+ }
+
+ final long estimatedBytes = dir.ramBytesUsed();
+ final long actualBytes = RamUsageTester.sizeOf(dir, DIRECTORY_ACCUMULATOR);
+ final double absoluteError = Math.abs(estimatedBytes - actualBytes);
+ final double relativeError = absoluteError / actualBytes;
+ assertTrue("Absolute error: " + absoluteError + ", relative error: " + relativeError,
+ relativeError < 0.20 || absoluteError < 4096);
+ for (IndexInput in : openInputs) {
+ in.close();
+ }
+ dir.close();
+ }
}
Index: solr/core/src/java/org/apache/solr/store/blockcache/BlockCache.java
===================================================================
--- solr/core/src/java/org/apache/solr/store/blockcache/BlockCache.java (revision 1611404)
+++ solr/core/src/java/org/apache/solr/store/blockcache/BlockCache.java (working copy)
@@ -21,6 +21,8 @@
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;
+import org.apache.lucene.util.Accountable;
+
import com.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap;
import com.googlecode.concurrentlinkedhashmap.EvictionListener;
@@ -27,7 +29,7 @@
/**
* @lucene.experimental
*/
-public class BlockCache {
+public class BlockCache implements Accountable {
public static final int _128M = 134217728;
public static final int _32K = 32768;
@@ -79,7 +81,12 @@
.maximumWeightedCapacity(maxEntries).listener(listener).build();
this.blockSize = blockSize;
}
-
+
+ @Override
+ public long ramBytesUsed() {
+ return (long) numberOfBlocksPerBank * blockSize * banks.length;
+ }
+
private void releaseLocation(BlockCacheLocation location) {
if (location == null) {
return;
Index: solr/core/src/java/org/apache/solr/store/blockcache/BlockDirectory.java
===================================================================
--- solr/core/src/java/org/apache/solr/store/blockcache/BlockDirectory.java (revision 1611404)
+++ solr/core/src/java/org/apache/solr/store/blockcache/BlockDirectory.java (working copy)
@@ -57,7 +57,11 @@
}
public static Cache NO_CACHE = new Cache() {
-
+
+ public long ramBytesUsed() {
+ return 0;
+ }
+
@Override
public void update(String name, long blockId, int blockOffset,
byte[] buffer, int offset, int length) {}
@@ -114,7 +118,12 @@
setLockFactory(directory.getLockFactory());
}
}
-
+
+ @Override
+ public long ramBytesUsed() {
+ return directory.ramBytesUsed() + cache.ramBytesUsed();
+ }
+
private IndexInput openInput(String name, int bufferSize, IOContext context)
throws IOException {
final IndexInput source = directory.openInput(name, context);
Index: solr/core/src/java/org/apache/solr/store/blockcache/BlockDirectoryCache.java
===================================================================
--- solr/core/src/java/org/apache/solr/store/blockcache/BlockDirectoryCache.java (revision 1611404)
+++ solr/core/src/java/org/apache/solr/store/blockcache/BlockDirectoryCache.java (working copy)
@@ -36,7 +36,12 @@
this.path = path;
this.metrics = metrics;
}
-
+
+ @Override
+ public long ramBytesUsed() {
+ return blockCache.ramBytesUsed();
+ }
+
/**
* Expert: mostly for tests
*
Index: solr/core/src/java/org/apache/solr/store/blockcache/Cache.java
===================================================================
--- solr/core/src/java/org/apache/solr/store/blockcache/Cache.java (revision 1611404)
+++ solr/core/src/java/org/apache/solr/store/blockcache/Cache.java (working copy)
@@ -1,5 +1,7 @@
package org.apache.solr.store.blockcache;
+import org.apache.lucene.util.Accountable;
+
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
@@ -20,7 +22,7 @@
/**
* @lucene.experimental
*/
-public interface Cache {
+public interface Cache extends Accountable {
/**
* Remove a file from the cache.
Index: solr/core/src/java/org/apache/solr/store/hdfs/HdfsDirectory.java
===================================================================
--- solr/core/src/java/org/apache/solr/store/hdfs/HdfsDirectory.java (revision 1611404)
+++ solr/core/src/java/org/apache/solr/store/hdfs/HdfsDirectory.java (working copy)
@@ -89,8 +89,13 @@
}
}
}
-
+
@Override
+ public long ramBytesUsed() {
+ return 0;
+ }
+
+ @Override
public void close() throws IOException {
LOG.info("Closing hdfs directory {}", hdfsDirPath);
fileSystem.close();
Index: solr/core/src/test/org/apache/solr/store/blockcache/BlockDirectoryTest.java
===================================================================
--- solr/core/src/test/org/apache/solr/store/blockcache/BlockDirectoryTest.java (revision 1611404)
+++ solr/core/src/test/org/apache/solr/store/blockcache/BlockDirectoryTest.java (working copy)
@@ -22,7 +22,6 @@
import java.util.Map;
import java.util.Random;
-import org.apache.commons.io.FileUtils;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.store.IOContext;
@@ -29,10 +28,8 @@
import org.apache.lucene.store.IndexInput;
import org.apache.lucene.store.IndexOutput;
import org.apache.lucene.store.MergeInfo;
-import org.apache.lucene.util.LuceneTestCase;
-
+import org.apache.lucene.util.RamUsageEstimator;
import org.apache.solr.SolrTestCaseJ4;
-import org.apache.solr.store.hdfs.HdfsDirectory;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -45,6 +42,15 @@
public Map<String, byte[]> map = new ConcurrentLinkedHashMap.Builder<String, byte[]>().maximumWeightedCapacity(8).build();
@Override
+ public long ramBytesUsed() {
+ long ramBytesUsed = map.size() * 2L * RamUsageEstimator.NUM_BYTES_OBJECT_REF;
+ for (byte[] array : map.values()) {
+ ramBytesUsed += RamUsageEstimator.sizeOf(array);
+ }
+ return ramBytesUsed;
+ }
+
+ @Override
public void update(String name, long blockId, int blockOffset, byte[] buffer, int offset, int length) {
byte[] cached = map.get(name + blockId);
if (cached != null) {