blob: 9f73e7c2c1d85d03977a50c25d6b89a682fbf8b0 [file] [log] [blame]
/*
* 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.geode.cache.query.internal.index;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.RegionAttributes;
import org.apache.geode.cache.query.IndexStatistics;
import org.apache.geode.cache.query.QueryService;
import org.apache.geode.internal.cache.BucketRegion;
import org.apache.geode.internal.cache.InternalCache;
import org.apache.geode.internal.cache.RegionEntry;
public class CompactMapRangeIndex extends AbstractMapIndex {
private final Map<Object, Map> entryToMapKeyIndexKeyMap;
private IndexCreationHelper ich;
CompactMapRangeIndex(InternalCache cache, String indexName, Region region, String fromClause,
String indexedExpression, String projectionAttributes, String origFromClause,
String origIndxExpr, String[] defintions, boolean isAllKeys,
String[] multiIndexingKeysPattern, Object[] mapKeys, IndexStatistics stats) {
super(cache, indexName, region, fromClause, indexedExpression, projectionAttributes,
origFromClause, origIndxExpr, defintions, isAllKeys, multiIndexingKeysPattern, mapKeys,
stats);
RegionAttributes ra = region.getAttributes();
this.entryToMapKeyIndexKeyMap = new java.util.concurrent.ConcurrentHashMap(
ra.getInitialCapacity(), ra.getLoadFactor(), ra.getConcurrencyLevel());
}
@Override
void instantiateEvaluator(IndexCreationHelper indexCreationHelper) {
this.evaluator = new IMQEvaluator(indexCreationHelper);
this.ich = indexCreationHelper;
}
@Override
public boolean containsEntry(RegionEntry entry) {
return this.entryToMapKeyIndexKeyMap.containsKey(entry);
}
@Override
void recreateIndexData() throws IMQException {
this.entryToMapKeyIndexKeyMap.clear();
this.mapKeyToValueIndex.clear();
this.initializeIndex(true);
}
@Override
protected void removeMapping(RegionEntry entry, int opCode) throws IMQException {
// this implementation has a reverse map, so it doesn't handle
// BEFORE_UPDATE_OP
if (opCode == BEFORE_UPDATE_OP || opCode == CLEAN_UP_THREAD_LOCALS) {
return;
}
// Object values = this.entryToMapKeysMap.remove(entry);
Map mapKeyToIndexKey = this.entryToMapKeyIndexKeyMap.remove(entry);
// Values in reverse coould be null if map in region value does not
// contain any key which matches to index expression keys.
if (mapKeyToIndexKey == null) {
return;
}
Iterator<Map.Entry<?, ?>> mapKeyIterator = mapKeyToIndexKey.entrySet().iterator();
while (mapKeyIterator.hasNext()) {
Map.Entry<?, ?> mapEntry = mapKeyIterator.next();
Object mapKey = mapEntry.getKey();
Object indexKey = mapEntry.getValue();
CompactRangeIndex ri = (CompactRangeIndex) this.mapKeyToValueIndex.get(mapKey);
long start = System.nanoTime();
this.internalIndexStats.incUpdatesInProgress(1);
ri.removeMapping(indexKey, entry);
this.internalIndexStats.incUpdatesInProgress(-1);
long end = System.nanoTime() - start;
this.internalIndexStats.incUpdateTime(end);
this.internalIndexStats.incNumUpdates();
}
}
@Override
void saveMapping(Object key, Object value, RegionEntry entry) throws IMQException {
if (key == QueryService.UNDEFINED || !(key instanceof Map)) {
return;
}
if (this.isAllKeys) {
Iterator<Map.Entry<?, ?>> entries = ((Map) key).entrySet().iterator();
while (entries.hasNext()) {
Map.Entry<?, ?> mapEntry = entries.next();
Object mapKey = mapEntry.getKey();
Object indexKey = mapEntry.getValue();
this.saveIndexAddition(mapKey, indexKey, value, entry);
}
removeOldMappings(((Map) key).keySet(), entry);
} else {
for (Object mapKey : mapKeys) {
Object indexKey = ((Map) key).get(mapKey);
if (indexKey != null) {
// Do not convert to IndexManager.NULL. We are only interested in specific keys
this.saveIndexAddition(mapKey, indexKey, value, entry);
}
}
}
}
private void removeOldMappings(Collection presentKeys, RegionEntry entry) throws IMQException {
Map oldKeysAndValuesForEntry = entryToMapKeyIndexKeyMap.get(entry);
if (oldKeysAndValuesForEntry == null) {
oldKeysAndValuesForEntry = Collections.EMPTY_MAP;
}
Set<Entry> removedKeyValueEntries = oldKeysAndValuesForEntry != null
? oldKeysAndValuesForEntry.entrySet() : Collections.EMPTY_SET;
Iterator<Entry> iterator = removedKeyValueEntries.iterator();
while (iterator.hasNext()) {
Entry keyValue = iterator.next();
Object indexKey = keyValue.getKey() == null ? IndexManager.NULL : keyValue.getKey();
if (!presentKeys.contains(indexKey)) {
CompactRangeIndex rg = (CompactRangeIndex) this.mapKeyToValueIndex.get(keyValue.getKey());
rg.removeMapping(keyValue.getValue(), entry);
iterator.remove();
}
}
}
@Override
protected void doIndexAddition(Object mapKey, Object indexKey, Object value, RegionEntry entry)
throws IMQException {
if (indexKey == null) {
indexKey = IndexManager.NULL;
}
if (mapKey == null) {
mapKey = IndexManager.NULL;
}
boolean isPr = this.region instanceof BucketRegion;
// Get RangeIndex for it or create it if absent
CompactRangeIndex rg = (CompactRangeIndex) this.mapKeyToValueIndex.get(mapKey);
if (rg == null) {
// use previously created MapRangeIndexStatistics
IndexStatistics stats = this.internalIndexStats;
PartitionedIndex prIndex = null;
if (isPr) {
prIndex = (PartitionedIndex) this.getPRIndex();
prIndex.incNumMapKeysStats(mapKey);
}
rg = new CompactRangeIndex(cache, indexName + "-" + mapKey, region, fromClause,
indexedExpression, projectionAttributes, this.originalFromClause,
this.originalIndexedExpression, this.canonicalizedDefinitions, stats);
rg.instantiateEvaluator(this.ich,
((AbstractIndex.IMQEvaluator) this.evaluator).getIndexResultSetType());
this.mapKeyToValueIndex.put(mapKey, rg);
if (!isPr) {
this.internalIndexStats.incNumMapIndexKeys(1);
}
}
long start = System.nanoTime();
rg.addMapping(indexKey, value, entry);
// This call is skipped when addMapping is called from MapRangeIndex
// rg.internalIndexStats.incNumUpdates();
this.internalIndexStats.incUpdatesInProgress(-1);
long end = System.nanoTime() - start;
this.internalIndexStats.incUpdateTime(end);
this.internalIndexStats.incNumUpdates();
// add to mapkey to indexkey map
Map mapKeyToIndexKey = this.entryToMapKeyIndexKeyMap.get(entry);
if (mapKeyToIndexKey == null) {
mapKeyToIndexKey = new HashMap();
entryToMapKeyIndexKeyMap.put(entry, mapKeyToIndexKey);
}
mapKeyToIndexKey.put(mapKey, indexKey);
}
@Override
protected void saveIndexAddition(Object mapKey, Object indexKey, Object value, RegionEntry entry)
throws IMQException {
if (indexKey == null) {
indexKey = IndexManager.NULL;
}
if (mapKey == null) {
mapKey = IndexManager.NULL;
}
boolean isPr = this.region instanceof BucketRegion;
// Get RangeIndex for it or create it if absent
CompactRangeIndex rg = (CompactRangeIndex) this.mapKeyToValueIndex.get(mapKey);
if (rg == null) {
// use previously created MapRangeIndexStatistics
IndexStatistics stats = this.internalIndexStats;
PartitionedIndex prIndex = null;
if (isPr) {
prIndex = (PartitionedIndex) this.getPRIndex();
prIndex.incNumMapKeysStats(mapKey);
}
rg = new CompactRangeIndex(cache, indexName + "-" + mapKey, region, fromClause,
indexedExpression, projectionAttributes, this.originalFromClause,
this.originalIndexedExpression, this.canonicalizedDefinitions, stats);
rg.instantiateEvaluator(this.ich,
((AbstractIndex.IMQEvaluator) this.evaluator).getIndexResultSetType());
this.mapKeyToValueIndex.put(mapKey, rg);
if (!isPr) {
this.internalIndexStats.incNumMapIndexKeys(1);
}
}
this.internalIndexStats.incUpdatesInProgress(1);
long start = System.nanoTime();
// add to mapkey to indexkey map
Map mapKeyToIndexKey = this.entryToMapKeyIndexKeyMap.get(entry);
if (mapKeyToIndexKey == null) {
mapKeyToIndexKey = new HashMap();
entryToMapKeyIndexKeyMap.put(entry, mapKeyToIndexKey);
}
// Due to the way indexes are stored, we are actually doing an "update" here
// and removing old keys that no longer exist for this region entry
Object oldKey = mapKeyToIndexKey.get(mapKey);
if (oldKey == null) {
rg.addMapping(indexKey, value, entry);
} else if (!oldKey.equals(indexKey)) {
rg.addMapping(indexKey, value, entry);
rg.removeMapping(oldKey, entry);
}
this.internalIndexStats.incUpdatesInProgress(-1);
long end = System.nanoTime() - start;
this.internalIndexStats.incUpdateTime(end);
this.internalIndexStats.incNumUpdates();
mapKeyToIndexKey.put(mapKey, indexKey);
}
}