mirror of
https://github.com/honeymoose/OpenSearch.git
synced 2025-03-25 01:19:02 +00:00
The `FieldDataWeighter` allowed to use a concrete subclass of the caches generic type to be used that causes ClassCastException and also trips the CirciutBreaker to not be decremented appropriately. This was tripped by settings randomization also part of this commit. Closes #6260
241 lines
10 KiB
Java
241 lines
10 KiB
Java
/*
|
|
* 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
|
|
*
|
|
* 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;
|
|
import com.google.common.cache.RemovalListener;
|
|
import com.google.common.cache.RemovalNotification;
|
|
import org.apache.lucene.index.AtomicReaderContext;
|
|
import org.apache.lucene.index.IndexReader;
|
|
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;
|
|
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;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
import java.util.concurrent.Callable;
|
|
|
|
/**
|
|
* A simple field data cache abstraction on the *index* level.
|
|
*/
|
|
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();
|
|
|
|
/**
|
|
* Clears all the field data stored cached in on this index for the specified field name.
|
|
*/
|
|
void clear(String fieldName);
|
|
|
|
void clear(Object coreCacheKey);
|
|
|
|
interface Listener {
|
|
|
|
void onLoad(FieldMapper.Names fieldNames, FieldDataType fieldDataType, RamUsage ramUsage);
|
|
|
|
void onUnload(FieldMapper.Names fieldNames, FieldDataType fieldDataType, boolean wasEvicted, long sizeInBytes);
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
private final FieldMapper.Names fieldNames;
|
|
private final FieldDataType fieldDataType;
|
|
private final Cache<Key, RamUsage> cache;
|
|
private final IndicesFieldDataCacheListener indicesFieldDataCacheListener;
|
|
private final ESLogger logger;
|
|
|
|
protected FieldBased(ESLogger logger, IndexService indexService, FieldMapper.Names fieldNames, FieldDataType fieldDataType, CacheBuilder cache, IndicesFieldDataCacheListener indicesFieldDataCacheListener) {
|
|
assert indexService != null;
|
|
this.logger = logger;
|
|
this.indexService = indexService;
|
|
this.fieldNames = fieldNames;
|
|
this.fieldDataType = fieldDataType;
|
|
this.indicesFieldDataCacheListener = indicesFieldDataCacheListener;
|
|
cache.removalListener(this);
|
|
//noinspection unchecked
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
@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());
|
|
//noinspection unchecked
|
|
return (FD) cache.get(key, new Callable<AtomicFieldData>() {
|
|
@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);
|
|
}
|
|
}
|
|
return fieldData;
|
|
}
|
|
});
|
|
}
|
|
|
|
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;
|
|
}
|
|
});
|
|
}
|
|
|
|
@Override
|
|
public void clear() {
|
|
cache.invalidateAll();
|
|
}
|
|
|
|
@Override
|
|
public void clear(String fieldName) {
|
|
cache.invalidateAll();
|
|
}
|
|
|
|
@Override
|
|
public void clear(Object coreCacheKey) {
|
|
cache.invalidate(new Key(coreCacheKey));
|
|
}
|
|
|
|
@Override
|
|
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;
|
|
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();
|
|
}
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|