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

org.apache.flink.runtime.state.gemini.engine.snapshot.SnapshotOperation 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.engine.snapshot;

import org.apache.flink.api.common.typeutils.base.MapSerializer;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.core.fs.FileSystem;
import org.apache.flink.core.fs.Path;
import org.apache.flink.core.memory.DataOutputView;
import org.apache.flink.core.memory.DataOutputViewStreamWrapper;
import org.apache.flink.runtime.state.gemini.engine.GRegionID;
import org.apache.flink.runtime.state.gemini.engine.dbms.GContext;
import org.apache.flink.runtime.state.gemini.engine.fs.FileManager;
import org.apache.flink.runtime.state.gemini.engine.fs.FileMeta;
import org.apache.flink.util.Preconditions;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nullable;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import static org.apache.flink.runtime.state.gemini.engine.snapshot.SnapshotMetaFile.writerFunc;

/**
 * Base snapshot operation.
 */
public abstract class SnapshotOperation {

	private static final Logger LOG = LoggerFactory.getLogger(SnapshotOperation.class);

	protected final GContext gContext;

	protected final SnapshotManager snapshotManager;

	/**
	 * Whether to force flush page when snapshotting, mainly used when rescale.
	 */
	private boolean forceFlushPage = false;

	SnapshotOperation(GContext gContext, SnapshotManager snapshotManager) {
		this.gContext = Preconditions.checkNotNull(gContext);
		this.snapshotManager = Preconditions.checkNotNull(snapshotManager);
	}

	public abstract SnapshotManager.PendingSnapshot createPendingSnapshot(
		BackendSnapshotMeta backendSnapshotMeta,
		long accessNumber);

	public abstract SnapshotManager.PendingSnapshot getPendingSnapshot();

	public abstract DBSnapshotResult getSnapshotResult() throws Exception;

	public void setForceFlushPage(boolean forceFlushPage) {
		this.forceFlushPage = forceFlushPage;
	}

	public boolean isForceFlushPage() {
		return forceFlushPage;
	}

	/**
	 * Write region snapshot meta and snapshot page index, this method would also update file mapping.
	 *
	 * @param fileMappings each file mapping is {groupId -> fileId, (reference, size)}
	 * @return The offsets of regions.
	 * @throws IOException
	 */
	List writeRegionMetaAndPageIndex(
			Map> tableRegionMap,
			List regionSnapshots,
			List>>> fileMappings) throws IOException {

		Preconditions.checkState(regionSnapshots.size() == fileMappings.size(),
			String.format("Unexpected size of region snapshots: %s v.s file mapping size %s.", regionSnapshots.size(), fileMappings.size()));

		int startRegionId = gContext.getStartRegionId();
		int endRegionId = gContext.getEndRegionId();
		int regionSize = endRegionId - startRegionId + 1;
		List regionOffsets = new ArrayList<>(regionSnapshots.size());
		for (int i = 0; i < regionSnapshots.size(); i++) {
			regionOffsets.add(new long[regionSize]);
		}

		// tables with index region.
		Set indexRegionTables = new HashSet<>();
		for (Map.Entry> entry : tableRegionMap.entrySet()) {
			boolean indexRegionExisted = entry.getValue().keySet().stream().anyMatch(gRegionId -> gRegionId.getIndexID() == GRegionID.G_REGION_INDEX_1);
			if (indexRegionExisted) {
				indexRegionTables.add(entry.getKey());
			}
		}
		List writers = regionSnapshots.stream().map(RegionSnapshot::getWriter).collect(Collectors.toList());

		// write non-empty table size
		writerFunc(writers, w -> w.writeInt(tableRegionMap.size()));
		for (int regionId = startRegionId; regionId <= endRegionId; regionId++) {

			// update writer position
			for (int i = 0; i < regionOffsets.size(); i++) {
				regionOffsets.get(i)[regionId - startRegionId] = writers.get(i).getPos();
			}

			// write group id
			int currentRegionId = regionId;
			writerFunc(writers, w -> w.writeInt(currentRegionId));

			for (Map.Entry> te : tableRegionMap.entrySet()) {
				String tableName = te.getKey();
				Map regionMetas = te.getValue();
				// write table name
				// TODO avoid to write table name directly
				writerFunc(writers, w -> w.writeUTF(tableName));
				// TODO not allways one or two regions
				int expectedRegions = indexRegionTables.contains(tableName) ? 2 : 1;
				int actualRegions = 0;

				for (Map.Entry re : regionMetas.entrySet()) {
					int groupId = re.getKey().getId();
					if (groupId == regionId) {
						if (actualRegions == 0) {
							writerFunc(writers, w -> w.writeInt(expectedRegions));
						}
						SnapshotManager.GRegionSnapshotMeta meta = re.getValue();
						// write region code
						writerFunc(writers, w -> w.writeInt(re.getKey().getRegionCode()));
						// write last seq id
						writerFunc(writers, w -> w.writeLong(meta.getLastSeqID()));
						// write removeAll seq id
						writerFunc(writers, w -> w.writeLong(meta.getRemoveAllSeqID()));
						// reset group id
						regionSnapshots.forEach(rs -> rs.reset(groupId));
						// flush all page index
						meta.getPageIndex().snapshot(regionSnapshots);
						// update file mapping after snapshot region
						for (int i = 0; i < fileMappings.size(); i++) {
							updateFileMapping(fileMappings.get(i), regionSnapshots.get(i), groupId);
						}

						actualRegions += 1;
						if (actualRegions == expectedRegions) {
							break;
						}
					}
				}

				if (actualRegions == 0) {
					// no region in meta matched for this region.
					writerFunc(writers, w -> w.writeInt(0));
				}
			}
		}

		return regionOffsets;
	}

	Tuple2 writeDfsAndLocalFileMapping(
		SnapshotManager.PendingSnapshot pendingSnapshot,
		SnapshotMetaFile.Writer writer,
		@Nullable FileManager localFileManager,
		FileManager dfsFileManager) throws IOException {

		// the offset of dfs file mapping in the meta file
		long dfsMappingOffset = writer.getPos();
		writeFileMapping(writer, dfsFileManager, pendingSnapshot.getSnapshotBasePath(),
			pendingSnapshot.getFileMapping(), false);

		// the offset of local file mapping in the meta file
		long localMappingOffset = writer.getPos();
		writeFileMapping(writer, localFileManager, pendingSnapshot.getLocalSnapshotBasePath(),
			pendingSnapshot.getLocalFileMapping(), true);

		// write the offset of dfs file mapping at the end of file for the usage to load snapshots.
		writer.writeLong(dfsMappingOffset);
		// write the offset of local file mapping at the end of file for the usage to load snapshots.
		writer.writeLong(localMappingOffset);
		return Tuple2.of(dfsMappingOffset, localMappingOffset);
	}

	void closeAndDeleteWriterQuietly(SnapshotMetaFile.Writer writer) {
		if (writer != null) {
			Path path = writer.getFilePath();
			try {
				writer.close();
			} catch (IOException ioe) {
				LOG.warn("Failed to close meta writer, {}, {}.", path.toUri().toString(), ioe);
			}
			try {
				FileSystem fileSystem = path.getFileSystem();
				fileSystem.delete(path, false);
			} catch (IOException ioe) {
				LOG.warn("Failed to delete meta file, {}, {}.", path.toUri().toString(), ioe);
			}
		}
	}

	private void writeFileMapping(
		SnapshotMetaFile.Writer writer,
		@Nullable FileManager fileManager,
		Path snapshotBasePath,
		Map>> fileMapping,
		boolean isLocal) throws IOException {

		if (fileManager == null) {
			writer.writeBoolean(false);
		} else {
			writer.writeBoolean(true);
			writer.writeInt(fileMapping.size());
			writer.writeUTF(snapshotBasePath.toUri().toString());
			if (fileMapping.size() > 0) {
				for (Integer fileId : fileMapping.keySet()) {
					FileMeta fileMeta = fileManager.getFileMeta(fileId);
					String path = fileMeta.getFilePath();
					// for local file mapping, only record the final component of the path
					writer.writeUTF(isLocal ? new Path(path).getName() : path);
					writer.writeInt(fileId);
					writer.writeLong(fileMeta.getFileSize());
				}
				MapSerializer>> fileMappingSerializer = ((SnapshotManagerImpl) snapshotManager).getFileMappingSerializer();
				DataOutputView dataOutputView = new DataOutputViewStreamWrapper(writer);
				fileMappingSerializer.serialize(fileMapping, dataOutputView);
			}
		}
	}

	private void updateFileMapping(
		Map>> fileMapping,
		RegionSnapshot regionSnapshot,
		int groupId) {

		// fileId -> (reference, size)
		Map> groupStat;
		for (Map.Entry> entry : regionSnapshot.getRegionFileMeta().entrySet()) {
			int fileId = entry.getKey();
			groupStat = fileMapping.computeIfAbsent(fileId, (nothing) -> new HashMap<>());
			Tuple2 tuple =
				groupStat.computeIfAbsent(groupId, (nothing) -> Tuple2.of(0, 0L));
			tuple.f0 += entry.getValue().f0;
			tuple.f1 += entry.getValue().f1;
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy