OpenSearch/src/main/java/org/elasticsearch/index/fielddata/IndexFieldDataCache.java

241 lines
10 KiB
Java
Raw Normal View History

2013-01-18 12:00:49 +01:00
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch 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
2013-01-18 12:00:49 +01:00
*
* 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.elasticsearch.index.fielddata;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
2013-01-24 11:38:18 +01:00
import com.google.common.cache.RemovalListener;
import com.google.common.cache.RemovalNotification;
2013-01-18 12:00:49 +01:00
import org.apache.lucene.index.AtomicReaderContext;
import org.apache.lucene.index.IndexReader;
2013-01-18 12:00:49 +01:00
import org.apache.lucene.index.SegmentReader;
import org.elasticsearch.common.logging.ESLogger;
import org.elasticsearch.common.lucene.SegmentReaderUtils;
import org.elasticsearch.index.fielddata.ordinals.GlobalOrdinalsIndexFieldData;
2013-01-24 11:38:18 +01:00
import org.elasticsearch.index.mapper.FieldMapper;
import org.elasticsearch.index.service.IndexService;
import org.elasticsearch.index.shard.ShardId;
import org.elasticsearch.index.shard.ShardUtils;
import org.elasticsearch.index.shard.service.IndexShard;
import org.elasticsearch.indices.fielddata.cache.IndicesFieldDataCacheListener;
2013-01-18 12:00:49 +01:00
import java.util.ArrayList;
import java.util.List;
2013-01-18 12:00:49 +01:00
import java.util.concurrent.Callable;
/**
* A simple field data cache abstraction on the *index* level.
2013-01-18 12:00:49 +01:00
*/
public interface IndexFieldDataCache {
<FD extends AtomicFieldData, IFD extends IndexFieldData<FD>> FD load(AtomicReaderContext context, IFD indexFieldData) throws Exception;
<IFD extends IndexFieldData.WithOrdinals<?>> IFD load(final IndexReader indexReader, final IFD indexFieldData) throws Exception;
/**
* Clears all the field data stored cached in on this index.
*/
void clear();
2013-01-18 12:00:49 +01:00
/**
* Clears all the field data stored cached in on this index for the specified field name.
*/
void clear(String fieldName);
2013-01-18 12:00:49 +01:00
2013-10-18 15:41:37 +02:00
void clear(Object coreCacheKey);
2013-01-24 11:38:18 +01:00
interface Listener {
void onLoad(FieldMapper.Names fieldNames, FieldDataType fieldDataType, RamUsage ramUsage);
2013-01-24 11:38:18 +01:00
void onUnload(FieldMapper.Names fieldNames, FieldDataType fieldDataType, boolean wasEvicted, long sizeInBytes);
2013-01-24 11:38:18 +01:00
}
2013-01-18 12:00:49 +01:00
/**
* The resident field data cache is a *per field* cache that keeps all the values in memory.
*/
static abstract class FieldBased implements IndexFieldDataCache, SegmentReader.CoreClosedListener, RemovalListener<FieldBased.Key, RamUsage>, IndexReader.ReaderClosedListener {
private final IndexService indexService;
2013-01-24 11:38:18 +01:00
private final FieldMapper.Names fieldNames;
private final FieldDataType fieldDataType;
private final Cache<Key, RamUsage> cache;
private final IndicesFieldDataCacheListener indicesFieldDataCacheListener;
private final ESLogger logger;
2013-01-18 12:00:49 +01:00
protected FieldBased(ESLogger logger, IndexService indexService, FieldMapper.Names fieldNames, FieldDataType fieldDataType, CacheBuilder cache, IndicesFieldDataCacheListener indicesFieldDataCacheListener) {
assert indexService != null;
this.logger = logger;
this.indexService = indexService;
2013-01-24 11:38:18 +01:00
this.fieldNames = fieldNames;
this.fieldDataType = fieldDataType;
this.indicesFieldDataCacheListener = indicesFieldDataCacheListener;
2013-01-24 11:38:18 +01:00
cache.removalListener(this);
//noinspection unchecked
2013-01-24 11:38:18 +01:00
this.cache = cache.build();
}
@Override
public void onRemoval(RemovalNotification<Key, RamUsage> notification) {
final Key key = notification.getKey();
assert key != null && key.listeners != null;
final RamUsage value = notification.getValue();
long sizeInBytes = key.sizeInBytes;
assert sizeInBytes >= 0 || value != null : "Expected size [" + sizeInBytes + "] to be positive or value [" + value + "] to be non-null";
if (sizeInBytes == -1 && value != null) {
sizeInBytes = value.getMemorySizeInBytes();
}
for (Listener listener : key.listeners) {
try {
listener.onUnload(fieldNames, fieldDataType, notification.wasEvicted(), sizeInBytes);
} catch (Throwable e) {
logger.error("Failed to call listener on field data cache unloading", e);
}
}
2013-01-18 12:00:49 +01:00
}
@Override
public <FD extends AtomicFieldData, IFD extends IndexFieldData<FD>> FD load(final AtomicReaderContext context, final IFD indexFieldData) throws Exception {
final Key key = new Key(context.reader().getCoreCacheKey());
2013-01-18 12:00:49 +01:00
//noinspection unchecked
return (FD) cache.get(key, new Callable<AtomicFieldData>() {
2013-01-18 12:00:49 +01:00
@Override
public AtomicFieldData call() throws Exception {
SegmentReaderUtils.registerCoreListener(context.reader(), FieldBased.this);
key.listeners.add(indicesFieldDataCacheListener);
final ShardId shardId = ShardUtils.extractShardId(context.reader());
if (shardId != null) {
final IndexShard shard = indexService.shard(shardId.id());
if (shard != null) {
key.listeners.add(shard.fieldData());
}
}
final AtomicFieldData fieldData = indexFieldData.loadDirect(context);
key.sizeInBytes = fieldData.getMemorySizeInBytes();
for (Listener listener : key.listeners) {
try {
listener.onLoad(fieldNames, fieldDataType, fieldData);
} catch (Throwable e) {
// load anyway since listeners should not throw exceptions
logger.error("Failed to call listener on atomic field data loading", e);
}
}
2013-01-24 11:38:18 +01:00
return fieldData;
2013-01-18 12:00:49 +01:00
}
});
}
public <IFD extends IndexFieldData.WithOrdinals<?>> IFD load(final IndexReader indexReader, final IFD indexFieldData) throws Exception {
final Key key = new Key(indexReader.getCoreCacheKey());
//noinspection unchecked
return (IFD) cache.get(key, new Callable<RamUsage>() {
@Override
public GlobalOrdinalsIndexFieldData call() throws Exception {
indexReader.addReaderClosedListener(FieldBased.this);
key.listeners.add(indicesFieldDataCacheListener);
final ShardId shardId = ShardUtils.extractShardId(indexReader);
if (shardId != null) {
IndexShard shard = indexService.shard(shardId.id());
if (shard != null) {
key.listeners.add(shard.fieldData());
}
}
GlobalOrdinalsIndexFieldData ifd = (GlobalOrdinalsIndexFieldData) indexFieldData.localGlobalDirect(indexReader);
key.sizeInBytes = ifd.getMemorySizeInBytes();
for (Listener listener : key.listeners) {
try {
listener.onLoad(fieldNames, fieldDataType, ifd);
} catch (Throwable e) {
// load anyway since listeners should not throw exceptions
logger.error("Failed to call listener on global ordinals loading", e);
}
}
return ifd;
}
});
}
2013-01-18 12:00:49 +01:00
@Override
public void clear() {
2013-01-18 12:00:49 +01:00
cache.invalidateAll();
}
@Override
public void clear(String fieldName) {
2013-01-18 12:00:49 +01:00
cache.invalidateAll();
}
@Override
2013-10-18 15:41:37 +02:00
public void clear(Object coreCacheKey) {
cache.invalidate(new Key(coreCacheKey));
}
@Override
2013-10-18 15:41:37 +02:00
public void onClose(Object coreCacheKey) {
cache.invalidate(new Key(coreCacheKey));
}
@Override
public void onClose(IndexReader reader) {
cache.invalidate(reader.getCoreCacheKey());
}
static class Key {
final Object readerKey;
2014-04-02 12:14:35 +07:00
final List<Listener> listeners = new ArrayList<>();
long sizeInBytes = -1; // optional size in bytes (we keep it here in case the values are soft references)
Key(Object readerKey) {
this.readerKey = readerKey;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
Key key = (Key) o;
if (!readerKey.equals(key.readerKey)) return false;
return true;
}
@Override
public int hashCode() {
return readerKey.hashCode();
}
}
2013-01-18 12:00:49 +01:00
}
static class Resident extends FieldBased {
public Resident(ESLogger logger, IndexService indexService, FieldMapper.Names fieldNames, FieldDataType fieldDataType, IndicesFieldDataCacheListener indicesFieldDataCacheListener) {
super(logger, indexService, fieldNames, fieldDataType, CacheBuilder.newBuilder(), indicesFieldDataCacheListener);
2013-01-18 12:00:49 +01:00
}
}
static class Soft extends FieldBased {
public Soft(ESLogger logger, IndexService indexService, FieldMapper.Names fieldNames, FieldDataType fieldDataType, IndicesFieldDataCacheListener indicesFieldDataCacheListener) {
super(logger, indexService, fieldNames, fieldDataType, CacheBuilder.newBuilder().softValues(), indicesFieldDataCacheListener);
2013-01-18 12:00:49 +01:00
}
}
}