/* * 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.cassandra.db.compaction; import java.io.File; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.cassandra.db.Directories; import org.apache.cassandra.io.sstable.Component; import org.apache.cassandra.io.sstable.Descriptor; import org.apache.cassandra.io.sstable.SSTableMetadata; import org.apache.cassandra.io.util.FileUtils; import org.apache.cassandra.utils.Pair; import org.codehaus.jackson.JsonNode; import org.codehaus.jackson.map.ObjectMapper; /** * This class was added to be able to migrate pre-CASSANDRA-4782 leveled manifests into the sstable metadata * * @deprecated since it can be removed in a future revision. */ @Deprecated public class LegacyLeveledManifest { private static final Logger logger = LoggerFactory.getLogger(LegacyLeveledManifest.class); private Map<Integer, Integer> sstableLevels; private LegacyLeveledManifest(File path) throws IOException { sstableLevels = new HashMap<Integer, Integer>(); ObjectMapper m = new ObjectMapper(); JsonNode rootNode = m.readValue(path, JsonNode.class); JsonNode generations = rootNode.get("generations"); assert generations.isArray(); for (JsonNode generation : generations) { int level = generation.get("generation").getIntValue(); JsonNode generationValues = generation.get("members"); for (JsonNode generationValue : generationValues) { sstableLevels.put(generationValue.getIntValue(), level); } } } private int levelOf(int sstableGeneration) { return sstableLevels.containsKey(sstableGeneration) ? sstableLevels.get(sstableGeneration) : 0; } /** * We need to migrate if there is a legacy leveledmanifest json-file * <p/> * If there is no jsonfile, we can just start as normally, sstable level will be at 0 for all sstables. * * @param keyspace * @param columnFamily * @return */ public static boolean manifestNeedsMigration(String keyspace, String columnFamily) { return Directories.create(keyspace, columnFamily).tryGetLeveledManifest() != null; } public static void migrateManifests(String keyspace, String columnFamily) throws IOException { logger.info("Migrating manifest for {}/{}", keyspace, columnFamily); snapshotWithoutCFS(keyspace, columnFamily); Directories directories = Directories.create(keyspace, columnFamily); File manifestFile = directories.tryGetLeveledManifest(); if (manifestFile == null) return; LegacyLeveledManifest legacyManifest = new LegacyLeveledManifest(manifestFile); for (Map.Entry<Descriptor, Set<Component>> entry : directories.sstableLister().includeBackups(false).skipTemporary(true).list().entrySet()) { Descriptor d = entry.getKey(); Pair<SSTableMetadata, Set<Integer>> oldMetadata = SSTableMetadata.serializer.deserialize(d, false); String metadataFilename = d.filenameFor(Component.STATS); LeveledManifest.mutateLevel(oldMetadata, d, metadataFilename, legacyManifest.levelOf(d.generation)); } FileUtils.deleteWithConfirm(manifestFile); } /** * Snapshot a CF without having to load the sstables in that directory * * @param keyspace * @param columnFamily * @throws IOException */ public static void snapshotWithoutCFS(String keyspace, String columnFamily) throws IOException { Directories directories = Directories.create(keyspace, columnFamily); String snapshotName = "pre-sstablemetamigration"; logger.info("Snapshotting {}, {} to {}", keyspace, columnFamily, snapshotName); for (Map.Entry<Descriptor, Set<Component>> entry : directories.sstableLister().includeBackups(false).skipTemporary(true).list().entrySet()) { Descriptor descriptor = entry.getKey(); File snapshotDirectoryPath = Directories.getSnapshotDirectory(descriptor, snapshotName); for (Component component : entry.getValue()) { File sourceFile = new File(descriptor.filenameFor(component)); File targetLink = new File(snapshotDirectoryPath, sourceFile.getName()); FileUtils.createHardLink(sourceFile, targetLink); } } File manifestFile = directories.tryGetLeveledManifest(); if (manifestFile != null) { File snapshotDirectory = new File(new File(manifestFile.getParentFile(), Directories.SNAPSHOT_SUBDIR), snapshotName); if (!snapshotDirectory.exists()) snapshotDirectory.mkdirs(); File target = new File(snapshotDirectory, manifestFile.getName()); FileUtils.createHardLink(manifestFile, target); } } }