All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.flink.runtime.state.gemini.keyed.AbstractGeminiKeyedMapStateImpl Maven / Gradle / Ivy

There is a newer version: 1.5.1
Show newest version
/*
 * 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.flink.runtime.state.gemini.keyed;

import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.runtime.state.StateStorage;
import org.apache.flink.runtime.state.gemini.engine.GRegion;
import org.apache.flink.runtime.state.gemini.engine.hashtable.AbstractGRegionKMapImpl;
import org.apache.flink.runtime.state.gemini.engine.hashtable.AbstractGTableKeyedMapImpl;
import org.apache.flink.runtime.state.gemini.engine.hashtable.GRegionKMapImpl;
import org.apache.flink.runtime.state.gemini.engine.metrics.StateMetrics;
import org.apache.flink.runtime.state.keyed.AbstractKeyedMapState;
import org.apache.flink.runtime.state.keyed.KeyedMapState;
import org.apache.flink.util.Preconditions;

import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * An implementation of {@link KeyedMapState} backed by a state storage.
 *
 * @param  Type of the keys in the state.
 * @param  Type of the map keys in the state.
 * @param  Type of theGemini map values in the state.
 */
public abstract class AbstractGeminiKeyedMapStateImpl>
	implements AbstractKeyedMapState {

	private final AbstractGTableKeyedMapImpl table;

	private final StateMetrics metrics;

	/**
	 * Constructor with the state storage to store mappings.
	 */
	public AbstractGeminiKeyedMapStateImpl(
		AbstractGTableKeyedMapImpl table,
		StateMetrics metrics
	) {
		this.table = Preconditions.checkNotNull(table);
		this.metrics = metrics;
	}

	//--------------------------------------------------------------------------

	@Override
	public boolean contains(K key) {
		if (key == null) {
			return false;
		}

		return getRegion(key).contains(key);
	}

	@Override
	public boolean contains(K key, MK mapKey) {
		if (key == null || mapKey == null) {
			return false;
		}

		return getRegion(key).contains(key, mapKey);
	}

	@Override
	public M get(K key) {
		return getOrDefault(key, null);
	}

	@SuppressWarnings("unchecked")
	@Override
	public M getOrDefault(K key, M defaultValue) {
		if (key == null) {
			return defaultValue;
		}

		if (metrics.checkAndUpdateKMapGetCounter()) {
			return getOrDefaultWithMetrics(key, defaultValue);
		} else {
			return doGetOrDefault(key, defaultValue);
		}
	}

	private M doGetOrDefault(K key, M defaultValue) {
		if (key == null) {
			return defaultValue;
		}

		Map result = getRegion(key).getOrDefault(key, null);
		return result == null ? defaultValue : (M) result;
	}

	private M getOrDefaultWithMetrics(K key, M defaultValue) {
		long start = System.nanoTime();
		M value = doGetOrDefault(key, defaultValue);
		long end = System.nanoTime();
		metrics.updateKMapGetHistogram(end - start);
		if (value != defaultValue && value != null) {
			metrics.updateKMapGetMapSizeHistogram(value.size());
		}

		return value;
	}

	@Override
	public MV get(K key, MK mapKey) {
		return getOrDefault(key, mapKey, null);
	}

	@SuppressWarnings("unchecked")
	@Override
	public MV getOrDefault(K key, MK mapKey, MV defaultMapValue) {
		if (key == null || mapKey == null) {
			return defaultMapValue;
		}

		if (metrics.checkAndUpdateKMapGetMkCounter()) {
			return getOrDefaultWithMetrics(key, mapKey, defaultMapValue);
		} else {
			return getRegion(key).getOrDefault(key, mapKey, defaultMapValue);
		}
	}

	private MV doGetOrDefault(K key, MK mapKey, MV defaultMapValue) {
		if (key == null || mapKey == null) {
			return defaultMapValue;
		}

		return getRegion(key).getOrDefault(key, mapKey, defaultMapValue);
	}

	private MV getOrDefaultWithMetrics(K key, MK mapKey, MV defaultMapValue) {
		long start = System.nanoTime();
		MV value = doGetOrDefault(key, mapKey, defaultMapValue);
		long end = System.nanoTime();
		metrics.updateKMapGetMkHistogram(end - start);

		return value;
	}

	@Override
	public Map getAll(Collection keys) {
		if (keys == null || keys.isEmpty()) {
			return new HashMap<>();
		}

		if (metrics.checkAndUpdateKMapMultiGetCounter()) {
			return getAllWithMetrics(keys);
		} else {
			return doGetAll(keys);
		}
	}

	private Map doGetAll(Collection keys) {
		Map results = new HashMap<>();
		for (K key : keys) {
			if (key == null) {
				continue;
			}

			M result = doGetOrDefault(key, null);
			if (result != null && !result.isEmpty()) {
				results.put(key, result);
			}
		}

		return results;
	}

	private Map getAllWithMetrics(Collection keys) {
		long start = System.nanoTime();
		Map value = doGetAll(keys);
		long end = System.nanoTime();
		metrics.updateKMapMultiGetHistogram(end - start);
		for (M o : value.values()) {
			if (o != null) {
				metrics.updateKMapGetMapSizeHistogram(o.size());
				// only select one map to update
				break;
			}
		}

		return value;
	}

	@SuppressWarnings("unchecked")
	@Override
	public M getAll(K key, Collection mapKeys) {
		if (key == null || mapKeys == null || mapKeys.isEmpty()) {
			return createMap();
		}

		if (metrics.checkAndUpdateKMapMultiGetCounter()) {
			return getAllWithMetrics(key, mapKeys);
		} else {
			return doGetAll(key, mapKeys);
		}
	}

	private M doGetAll(K key, Collection mapKeys) {
		if (key == null || mapKeys == null || mapKeys.isEmpty()) {
			return createMap();
		}

		// TODO performance
		M results = createMap();

		for (MK mapKey : mapKeys) {
			if (mapKey != null) {
				MV value = doGetOrDefault(key, mapKey, null);
				if (value != null) {
					results.put(mapKey, value);
				}
			}
		}
		return results;
	}

	private M getAllWithMetrics(K key, Collection mapKeys) {
		long start = System.nanoTime();
		M value = doGetAll(key, mapKeys);
		long end = System.nanoTime();
		metrics.updateKMapMultiGetHistogram(end - start);

		return value;
	}

	@SuppressWarnings("unchecked")
	@Override
	public Map getAll(Map> map) {
		if (map == null || map.isEmpty()) {
			return Collections.emptyMap();
		}

		if (metrics.checkAndUpdateKMapMultiGetCounter()) {
			return getAllWithMetrics(map);
		} else {
			return doGetAll(map);
		}
	}

	private Map doGetAll(Map> map) {
		Map results = new HashMap<>();

		for (Map.Entry> entry : map.entrySet()) {
			K key = entry.getKey();
			Collection mapKeys = entry.getValue();
			M resultMap = doGetAll(key, mapKeys);
			if (!resultMap.isEmpty()) {
				results.put(key, doGetAll(key, mapKeys));
			}
		}
		return results;
	}

	private Map getAllWithMetrics(Map> map) {
		long start = System.nanoTime();
		Map  value = doGetAll(map);
		long end = System.nanoTime();
		metrics.updateKMapMultiGetHistogram(end - start);

		return value;
	}

	@Override
	public void add(K key, MK mapKey, MV mapValue) {
		Preconditions.checkNotNull(key);

		if (metrics.checkAndUpdateKMapPutCounter()) {
			addWithMetrics(key, mapKey, mapValue);
		} else {
			getRegion(key).add(key, mapKey, mapValue);
		}
	}

	private void addWithMetrics(K key, MK mapKey, MV mapValue) {
		long start = System.nanoTime();
		getRegion(key).add(key, mapKey, mapValue);
		long end = System.nanoTime();
		metrics.updateKMapPutHistogram(end - start);
	}

	@Override
	public void addAll(K key, Map mappings) {
		Preconditions.checkNotNull(key);

		if (mappings == null || mappings.isEmpty()) {
			return;
		}

		if (metrics.checkAndUpdateKMapMultiPutCounter()) {
			addAllWithMetrics(key, mappings);
		} else {
			doAddAll(key, mappings);
		}
	}

	private void doAddAll(K key, Map mappings) {
		Preconditions.checkNotNull(key);

		if (mappings == null || mappings.isEmpty()) {
			return;
		}

		getRegion(key).addAll(key, mappings);
	}

	private void addAllWithMetrics(K key, Map mappings) {
		long start = System.nanoTime();
		getRegion(key).addAll(key, mappings);
		long end = System.nanoTime();
		metrics.updateKMapMultiPutHistogram(end - start);
	}

	@Override
	public void addAll(Map> map) {
		if (map == null || map.isEmpty()) {
			return;
		}

		if (metrics.checkAndUpdateKMapMultiPutCounter()) {
			addAllWithMetrics(map);
		} else {
			for (Map.Entry entry : map.entrySet()) {
				doAddAll((K) entry.getKey(), (Map) entry.getValue());
			}
		}
	}

	private void addAllWithMetrics(Map> map) {
		long start = System.nanoTime();
		for (Map.Entry entry : map.entrySet()) {
			doAddAll((K) entry.getKey(), (Map) entry.getValue());
		}
		long end = System.nanoTime();
		metrics.updateKMapMultiPutHistogram(end - start);
	}

	@Override
	public void remove(K key) {
		if (key == null) {
			return;
		}

		if (metrics.checkAndUpdateKMapRemoveCounter()) {
			removeWithMetrics(key);
		} else {
			getRegion(key).remove(key);
		}
	}

	private void removeWithMetrics(K key) {
		long start = System.nanoTime();
		getRegion(key).remove(key);
		long end = System.nanoTime();
		metrics.updateKMapRemoveHistogram(end - start);
	}

	@Override
	public void remove(K key, MK mapKey) {
		if (key == null) {
			return;
		}

		if (metrics.checkAndUpdateKMapRemoveCounter()) {
			removeWithMetrics(key, mapKey);
		} else {
			getRegion(key).remove(key, mapKey);
		}
	}

	private void removeWithMetrics(K key, MK mapKey) {
		long start = System.nanoTime();
		getRegion(key).remove(key, mapKey);
		long end = System.nanoTime();
		metrics.updateKMapRemoveHistogram(end - start);
	}

	@Override
	public void removeAll(Collection keys) {
		if (keys == null || keys.isEmpty()) {
			return;
		}

		if (metrics.checkAndUpdateKMapMultiRemoveCounter()) {
			removeAllWithMetrics(keys);
		} else {
			for (K key : keys) {
				if (key != null) {
					getRegion(key).remove(key);
				}
			}
		}
	}

	private void removeAllWithMetrics(Collection keys) {
		long start = System.nanoTime();
		for (K key : keys) {
			if (key != null) {
				getRegion(key).remove(key);
			}
		}
		long end = System.nanoTime();
		metrics.updateKMapMultiRemoveHistogram(end - start);
	}

	@Override
	public void removeAll(K key, Collection mapKeys) {
		if (key == null || mapKeys == null || mapKeys.isEmpty()) {
			return;
		}

		if (metrics.checkAndUpdateKMapMultiRemoveCounter()) {
			removeAllWithMetrics(key, mapKeys);
		} else {
			doRemoveAll(key, mapKeys);
		}
	}

	private void doRemoveAll(K key, Collection mapKeys) {
		if (key == null || mapKeys == null || mapKeys.isEmpty()) {
			return;
		}

		// TODO performance
		for (MK mapKey : mapKeys) {
			if (mapKey != null) {
				getRegion(key).remove(key, mapKey);
			}
		}
	}

	private void removeAllWithMetrics(K key, Collection mapKeys) {
		long start = System.nanoTime();
		// TODO performance
		doRemoveAll(key, mapKeys);
		long end = System.nanoTime();
		metrics.updateKMapMultiRemoveHistogram(end - start);
	}

	@Override
	public void removeAll(Map> map) {
		if (map == null || map.isEmpty()) {
			return;
		}

		if (metrics.checkAndUpdateKMapMultiRemoveCounter()) {
			removeAllWithMetrics(map);
		} else {
			for (Map.Entry> entry : map.entrySet()) {
				doRemoveAll(entry.getKey(), entry.getValue());
			}
		}
	}

	private void removeAllWithMetrics(Map> map) {
		long start = System.nanoTime();
		for (Map.Entry> entry : map.entrySet()) {
			doRemoveAll(entry.getKey(), entry.getValue());
		}
		long end = System.nanoTime();
		metrics.updateKMapMultiRemoveHistogram(end - start);
	}

	@Override
	public Iterator> iterator(K key) {
		Preconditions.checkNotNull(key);

		if (metrics.checkAndUpdateKMapIteratorCounter()) {
			return iteratorWithMetrics(key);
		} else {
			return doIterator(key).f0;
		}
	}

	private Tuple2>, Integer> doIterator(K key) {
		// TODO performance
		Map res = getRegion(key).get(key);
		if (res == null) {
			return Tuple2.of(Collections.emptyIterator(), -1);
		} else {
			return Tuple2.of(new IteratorWrapper(key, res.entrySet().iterator()), res.size());
		}
	}

	private Iterator> iteratorWithMetrics(K key) {
		long start = System.nanoTime();
		Tuple2>, Integer> result = doIterator(key);
		long end = System.nanoTime();
		metrics.updateKMapIteratorHistogram(end - start);
		if (result.f1 >= 0) {
			metrics.updateKMapGetMapSizeHistogram(result.f1);
		}

		return result.f0;
	}

	@Override
	public Iterable> entries(K key) {
		Preconditions.checkNotNull(key);

		// TODO performance
		return () -> iterator(key);
	}

	@Override
	public Iterable mapKeys(K key) {
		Preconditions.checkNotNull(key);

		// TODO there is no need to deserialize map value
		return () -> new Iterator() {

			Iterator> iterator = iterator(key);

			@Override
			public boolean hasNext() {
				return iterator.hasNext();
			}

			@Override
			public MK next() {
				return iterator.next().getKey();
			}

			@Override
			public void remove() {
				iterator.remove();
			}
		};
	}

	@Override
	public Iterable mapValues(K key) {
		Preconditions.checkNotNull(key);

		// TODO there is no need to deserialize map key
		return () -> new Iterator() {

			Iterator> iterator = iterator(key);

			@Override
			public boolean hasNext() {
				return iterator.hasNext();
			}

			@Override
			public MV next() {
				return iterator.next().getValue();
			}

			@Override
			public void remove() {
				iterator.remove();
			}
		};
	}

	@Override
	public Map getAll() {
		if (metrics.checkAndUpdateKMapMultiGetCounter()) {
			return getAllWithMetrics();
		} else {
			return doGetAll();
		}
	}

	private Map doGetAll() {
		Map> results = new HashMap<>();
		Iterator iterator = table.dataRegionIterator();
		while (iterator.hasNext()) {
			GRegionKMapImpl region = (GRegionKMapImpl) iterator.next();
			region.getAll(results);
		}
		return (Map) results;
	}

	private Map getAllWithMetrics() {
		long start = System.nanoTime();
		Map value = doGetAll();
		long end = System.nanoTime();
		metrics.updateKMapMultiGetHistogram(end - start);
		for (M o : value.values()) {
			if (o != null) {
				metrics.updateKMapGetMapSizeHistogram(o.size());
				// only select one map to update
				break;
			}
		}

		return value;
	}

	@Override
	public void removeAll() {
		if (metrics.checkAndUpdateKMapMultiRemoveCounter()) {
			removeAllWithMetrics();
		} else {
			doRemoveAll();
		}
	}

	private void doRemoveAll() {
		Iterator iterator = table.dataRegionIterator();
		while (iterator.hasNext()) {
			GRegionKMapImpl region = (GRegionKMapImpl) iterator.next();
			region.removeAll();
		}
	}

	private void removeAllWithMetrics() {
		long start = System.nanoTime();
		doRemoveAll();
		long end = System.nanoTime();
		metrics.updateKMapMultiRemoveHistogram(end - start);
	}

	@Override
	public Iterable keys() {
		// TODO complexity
		return getAll().keySet();
	}

	@Override
	public StateStorage getStateStorage() {
		throw new UnsupportedOperationException();
	}

	@SuppressWarnings("unchecked")
	protected AbstractGRegionKMapImpl> getRegion(K key) {
		return (AbstractGRegionKMapImpl>) table.getRegion(key);
	}

	abstract M createMap();

	/**
	 * A map entry for GeminiMapState and GeminiSortedMapState.
	 * TODO store the serialized map key/value and lazily deserialize them like RocksDBMapEntry.
	 */
	class GeminiMapEntry implements Map.Entry {

		/** True if the entry has been deleted. */
		private boolean deleted;

		private K key;

		private MK mapKey;

		private MV mapValue;

		GeminiMapEntry(K key, MK mapKey, MV mapValue) {
			this.key = key;
			this.mapKey = mapKey;
			this.mapValue = mapValue;
			this.deleted = false;
		}

		public void remove() {
			if (deleted) {
				throw new IllegalStateException("Entry has been removed.");
			}

			deleted = true;
			mapValue = null;

			// TODO reduce the cost of serialization of key and map key
			AbstractGeminiKeyedMapStateImpl.this.remove(key, mapKey);
		}

		@Override
		public MK getKey() {
			return mapKey;
		}

		@Override
		public MV getValue() {
			return mapValue;
		}

		@Override
		public MV setValue(MV value) {
			if (deleted) {
				throw new IllegalStateException("The value has already been deleted.");
			}

			MV oldValue = mapValue;
			mapValue = value;
			AbstractGeminiKeyedMapStateImpl.this.add(key, mapKey, value);

			return oldValue;
		}
	}

	/**
	 * An iterator wrapper to support remove.
	 */
	class IteratorWrapper implements Iterator> {

		private K key;

		Iterator> iterator;

		GeminiMapEntry currentEntry;

		IteratorWrapper(K key, Iterator> iterator) {
			this.key = key;
			this.iterator = iterator;
		}

		@Override
		public boolean hasNext() {
			return iterator.hasNext();
		}

		@Override
		public Map.Entry next() {
			Map.Entry entry = iterator.next();
			currentEntry = new GeminiMapEntry(key, entry.getKey(), entry.getValue());

			return currentEntry;
		}

		@Override
		public void remove() {
			if (currentEntry == null) {
				throw new IllegalStateException("The remove operation must be called after a valid next operation.");
			}

			currentEntry.remove();
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy