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

org.apache.flink.runtime.state.heap.NestedStateMap Maven / Gradle / Ivy

There is a newer version: 1.13.6
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.heap;

import org.apache.flink.runtime.state.StateEntry;
import org.apache.flink.runtime.state.StateTransformationFunction;
import org.apache.flink.runtime.state.internal.InternalKvState;

import javax.annotation.Nonnull;

import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.stream.Stream;

/**
 * This implementation of {@link StateMap} uses nested {@link HashMap} objects.
 *
 * @param  type of key.
 * @param  type of namespace.
 * @param  type of value.
 */
public class NestedStateMap extends StateMap {

	/**
	 * Map for holding the actual state objects. The nested map provide
	 * an outer scope by namespace and an inner scope by key.
	 */
	private final Map> namespaceMap;

	/**
	 * Constructs a new {@code NestedStateMap}.
	 */
	public NestedStateMap() {
		this.namespaceMap = new HashMap<>();
	}

	// Public API from StateMap ------------------------------------------------------------------------------

	@Override
	public int size() {
		int count = 0;
		for (Map keyMap : namespaceMap.values()) {
			if (null != keyMap) {
				count += keyMap.size();
			}
		}

		return count;
	}

	@Override
	public S get(K key, N namespace) {
		Map keyedMap = namespaceMap.get(namespace);

		if (keyedMap == null) {
			return null;
		}

		return keyedMap.get(key);
	}

	@Override
	public boolean containsKey(K key, N namespace) {
		Map keyedMap = namespaceMap.get(namespace);

		return keyedMap != null && keyedMap.containsKey(key);
	}

	@Override
	public void put(K key, N namespace, S state) {
		putAndGetOld(key, namespace, state);
	}

	@Override
	public S putAndGetOld(K key, N namespace, S state) {
		Map keyedMap = namespaceMap.computeIfAbsent(namespace, k -> new HashMap<>());

		return keyedMap.put(key, state);
	}

	@Override
	public void remove(K key, N namespace) {
		removeAndGetOld(key, namespace);
	}

	@Override
	public S removeAndGetOld(K key, N namespace) {
		Map keyedMap = namespaceMap.get(namespace);

		if (keyedMap == null) {
			return null;
		}

		S removed = keyedMap.remove(key);

		if (keyedMap.isEmpty()) {
			namespaceMap.remove(namespace);
		}

		return removed;
	}

	@Override
	public  void transform(
		K key, N namespace, T value, StateTransformationFunction transformation) throws Exception {
		Map keyedMap = namespaceMap.computeIfAbsent(namespace, k -> new HashMap<>());
		keyedMap.put(key, transformation.apply(keyedMap.get(key), value));
	}

	@Override
	public Iterator> iterator() {
		return new StateEntryIterator();
	}

	@Override
	public Stream getKeys(N namespace) {
		return namespaceMap.getOrDefault(namespace, Collections.emptyMap()).keySet().stream();

	}

	@Override
	public InternalKvState.StateIncrementalVisitor getStateIncrementalVisitor(
		int recommendedMaxNumberOfReturnedRecords) {
		return new StateEntryVisitor();
	}

	@Override
	public int sizeOfNamespace(Object namespace) {
		Map keyMap = namespaceMap.get(namespace);
		return keyMap != null ? keyMap.size() : 0;
	}

	@Nonnull
	@Override
	public StateMapSnapshot> stateSnapshot() {
		return new NestedStateMapSnapshot<>(this);
	}

	public Map> getNamespaceMap() {
		return namespaceMap;
	}

	/**
	 * Iterator over state entries in a {@link NestedStateMap}.
	 */
	class StateEntryIterator implements Iterator> {
		private Iterator>> namespaceIterator;
		private Map.Entry> namespace;
		private Iterator> keyValueIterator;

		StateEntryIterator() {
			namespaceIterator = namespaceMap.entrySet().iterator();
			namespace = null;
			keyValueIterator = Collections.emptyIterator();
		}

		@Override
		public boolean hasNext() {
			return keyValueIterator.hasNext() || namespaceIterator.hasNext();
		}

		@Override
		public StateEntry next() {
			if (!hasNext()) {
				throw new NoSuchElementException();
			}

			if (!keyValueIterator.hasNext()) {
				namespace = namespaceIterator.next();
				keyValueIterator = namespace.getValue().entrySet().iterator();
			}

			Map.Entry entry = keyValueIterator.next();

			return new StateEntry.SimpleStateEntry<>(
				entry.getKey(), namespace.getKey(), entry.getValue());
		}
	}


	/**
	 * Incremental visitor over state entries in a {@link NestedStateMap}.
	 *
	 * 

The iterator keeps a snapshotted copy of key/namespace sets, available at the beginning of iteration. * While further iterating the copy, the iterator returns the actual state value from primary maps * if exists at that moment. * *

Note: Usage of this iterator can have a heap memory consumption impact. */ class StateEntryVisitor implements InternalKvState.StateIncrementalVisitor, Iterator> { private Iterator>> namespaceIterator; private Map.Entry> namespace; private Iterator> keyValueIterator; private StateEntry nextEntry; private StateEntry lastReturnedEntry; StateEntryVisitor() { namespaceIterator = new HashSet<>(namespaceMap.entrySet()).iterator(); namespace = null; keyValueIterator = null; nextKeyIterator(); } @Override public boolean hasNext() { nextKeyIterator(); return keyIteratorHasNext(); } @Override public Collection> nextEntries() { StateEntry nextEntry = next(); return nextEntry == null ? Collections.emptyList() : Collections.singletonList(nextEntry); } @Override public StateEntry next() { StateEntry next = null; if (hasNext()) { next = nextEntry; } nextEntry = null; lastReturnedEntry = next; return next; } private void nextKeyIterator() { while (!keyIteratorHasNext()) { if (namespaceIteratorHasNext()) { namespace = namespaceIterator.next(); keyValueIterator = new HashSet<>(namespace.getValue().entrySet()).iterator(); } else { break; } } } private boolean keyIteratorHasNext() { while (nextEntry == null && keyValueIterator != null && keyValueIterator.hasNext()) { Map.Entry next = keyValueIterator.next(); Map ns = namespaceMap.getOrDefault(namespace.getKey(), null); S upToDateValue = ns == null ? null : ns.getOrDefault(next.getKey(), null); if (upToDateValue != null) { nextEntry = new StateEntry.SimpleStateEntry<>(next.getKey(), namespace.getKey(), upToDateValue); } } return nextEntry != null; } private boolean namespaceIteratorHasNext() { return namespaceIterator.hasNext(); } @Override public void remove() { remove(lastReturnedEntry); } @Override public void remove(StateEntry stateEntry) { namespaceMap.get(stateEntry.getNamespace()).remove(stateEntry.getKey()); } @Override public void update(StateEntry stateEntry, S newValue) { namespaceMap.get(stateEntry.getNamespace()).put(stateEntry.getKey(), newValue); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy