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

org.elasticsearch.gateway.MetaStateService Maven / Gradle / Ivy

There is a newer version: 8.14.0
Show newest version
/*
 * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
 * or more contributor license agreements. Licensed under the Elastic License
 * 2.0 and the Server Side Public License, v 1; you may not use this file except
 * in compliance with, at your election, the Elastic License 2.0 or the Server
 * Side Public License, v 1.
 */

package org.elasticsearch.gateway;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.elasticsearch.Version;
import org.elasticsearch.cluster.metadata.IndexGraveyard;
import org.elasticsearch.cluster.metadata.IndexMetadata;
import org.elasticsearch.cluster.metadata.Manifest;
import org.elasticsearch.cluster.metadata.Metadata;
import org.elasticsearch.core.Nullable;
import org.elasticsearch.core.Tuple;
import org.elasticsearch.env.NodeEnvironment;
import org.elasticsearch.index.Index;
import org.elasticsearch.xcontent.NamedXContentRegistry;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;

/**
 * Handles writing and loading {@link Manifest}, {@link Metadata} and {@link IndexMetadata}
 */
public class MetaStateService {
    private static final Logger logger = LogManager.getLogger(MetaStateService.class);

    private final NodeEnvironment nodeEnv;
    private final NamedXContentRegistry namedXContentRegistry;

    // we allow subclasses in tests to redefine formats, e.g. to inject failures
    protected MetadataStateFormat METADATA_FORMAT = Metadata.FORMAT;
    protected MetadataStateFormat INDEX_METADATA_FORMAT = IndexMetadata.FORMAT;
    protected MetadataStateFormat MANIFEST_FORMAT = Manifest.FORMAT;

    public MetaStateService(NodeEnvironment nodeEnv, NamedXContentRegistry namedXContentRegistry) {
        this.nodeEnv = nodeEnv;
        this.namedXContentRegistry = namedXContentRegistry;
    }

    /**
     * Loads the full state, which includes both the global state and all the indices meta data. 
* When loading, manifest file is consulted (represented by {@link Manifest} class), to load proper generations.
* If there is no manifest file on disk, this method fallbacks to BWC mode, where latest generation of global and indices * metadata is loaded. Please note that currently there is no way to distinguish between manifest file being removed and manifest * file was not yet created. It means that this method always fallbacks to BWC mode, if there is no manifest file. * * @return tuple of {@link Manifest} and {@link Metadata} with global metadata and indices metadata. If there is no state on disk, * meta state with globalGeneration -1 and empty meta data is returned. * @throws IOException if some IOException when loading files occurs or there is no metadata referenced by manifest file. */ public Tuple loadFullState() throws IOException { final Manifest manifest = MANIFEST_FORMAT.loadLatestState(logger, namedXContentRegistry, nodeEnv.nodeDataPaths()); if (manifest == null) { return loadFullStateBWC(); } final Metadata.Builder metadataBuilder; if (manifest.isGlobalGenerationMissing()) { metadataBuilder = Metadata.builder(); } else { final Metadata globalMetadata = METADATA_FORMAT.loadGeneration( logger, namedXContentRegistry, manifest.getGlobalGeneration(), nodeEnv.nodeDataPaths() ); if (globalMetadata != null) { metadataBuilder = Metadata.builder(globalMetadata); } else { throw new IOException("failed to find global metadata [generation: " + manifest.getGlobalGeneration() + "]"); } } for (Map.Entry entry : manifest.getIndexGenerations().entrySet()) { final Index index = entry.getKey(); final long generation = entry.getValue(); final String indexFolderName = index.getUUID(); final IndexMetadata indexMetadata = INDEX_METADATA_FORMAT.loadGeneration( logger, namedXContentRegistry, generation, nodeEnv.resolveIndexFolder(indexFolderName) ); if (indexMetadata != null) { metadataBuilder.put(indexMetadata, false); } else { throw new IOException( "failed to find metadata for existing index " + index.getName() + " [location: " + indexFolderName + ", generation: " + generation + "]" ); } } return new Tuple<>(manifest, metadataBuilder.build()); } /** * "Manifest-less" BWC version of loading metadata from disk. See also {@link #loadFullState()} */ private Tuple loadFullStateBWC() throws IOException { Map indices = new HashMap<>(); Metadata.Builder metadataBuilder; Tuple metadataAndGeneration = METADATA_FORMAT.loadLatestStateWithGeneration( logger, namedXContentRegistry, nodeEnv.nodeDataPaths() ); Metadata globalMetadata = metadataAndGeneration.v1(); long globalStateGeneration = metadataAndGeneration.v2(); final IndexGraveyard indexGraveyard; if (globalMetadata != null) { metadataBuilder = Metadata.builder(globalMetadata); indexGraveyard = globalMetadata.custom(IndexGraveyard.TYPE); assert Version.CURRENT.major < 8 : "failed to find manifest file, which is mandatory staring with Elasticsearch version 8.0"; } else { metadataBuilder = Metadata.builder(); indexGraveyard = IndexGraveyard.builder().build(); } for (String indexFolderName : nodeEnv.availableIndexFolders()) { Tuple indexMetadataAndGeneration = INDEX_METADATA_FORMAT.loadLatestStateWithGeneration( logger, namedXContentRegistry, nodeEnv.resolveIndexFolder(indexFolderName) ); assert Version.CURRENT.major < 8 : "failed to find manifest file, which is mandatory staring with Elasticsearch version 8.0"; IndexMetadata indexMetadata = indexMetadataAndGeneration.v1(); long generation = indexMetadataAndGeneration.v2(); if (indexMetadata != null) { if (indexGraveyard.containsIndex(indexMetadata.getIndex())) { logger.debug("[{}] found metadata for deleted index [{}]", indexFolderName, indexMetadata.getIndex()); // this index folder is cleared up when state is recovered } else { indices.put(indexMetadata.getIndex(), generation); metadataBuilder.put(indexMetadata, false); } } else { logger.debug("[{}] failed to find metadata for existing index location", indexFolderName); } } Manifest manifest = Manifest.unknownCurrentTermAndVersion(globalStateGeneration, indices); return new Tuple<>(manifest, metadataBuilder.build()); } /** * Loads the index state for the provided index name, returning null if doesn't exists. */ @Nullable public IndexMetadata loadIndexState(Index index) throws IOException { return INDEX_METADATA_FORMAT.loadLatestState(logger, namedXContentRegistry, nodeEnv.indexPaths(index)); } /** * Loads all indices states available on disk */ List loadIndicesStates(Predicate excludeIndexPathIdsPredicate) throws IOException { List indexMetadataList = new ArrayList<>(); for (String indexFolderName : nodeEnv.availableIndexFolders(excludeIndexPathIdsPredicate)) { assert excludeIndexPathIdsPredicate.test(indexFolderName) == false : "unexpected folder " + indexFolderName + " which should have been excluded"; IndexMetadata indexMetadata = INDEX_METADATA_FORMAT.loadLatestState( logger, namedXContentRegistry, nodeEnv.resolveIndexFolder(indexFolderName) ); if (indexMetadata != null) { final String indexPathId = indexMetadata.getIndex().getUUID(); if (indexFolderName.equals(indexPathId)) { indexMetadataList.add(indexMetadata); } else { throw new IllegalStateException("[" + indexFolderName + "] invalid index folder name, rename to [" + indexPathId + "]"); } } else { logger.debug("[{}] failed to find metadata for existing index location", indexFolderName); } } return indexMetadataList; } /** * Loads Manifest file from disk, returns Manifest.empty() if there is no manifest file. */ public Manifest loadManifestOrEmpty() throws IOException { Manifest manifest = MANIFEST_FORMAT.loadLatestState(logger, namedXContentRegistry, nodeEnv.nodeDataPaths()); if (manifest == null) { manifest = Manifest.empty(); } return manifest; } /** * Loads the global state, *without* index state, see {@link #loadFullState()} for that. */ Metadata loadGlobalState() throws IOException { return METADATA_FORMAT.loadLatestState(logger, namedXContentRegistry, nodeEnv.nodeDataPaths()); } /** * Writes manifest file (represented by {@link Manifest}) to disk and performs cleanup of old manifest state file if * the write succeeds or newly created manifest state if the write fails. * * @throws WriteStateException if exception when writing state occurs. See also {@link WriteStateException#isDirty()} */ public void writeManifestAndCleanup(String reason, Manifest manifest) throws WriteStateException { logger.trace("[_meta] writing state, reason [{}]", reason); try { long generation = MANIFEST_FORMAT.writeAndCleanup(manifest, nodeEnv.nodeDataPaths()); logger.trace("[_meta] state written (generation: {})", generation); } catch (WriteStateException ex) { throw new WriteStateException(ex.isDirty(), "[_meta]: failed to write meta state", ex); } } /** * Writes the index state. *

* This method is public for testing purposes. * * @throws WriteStateException if exception when writing state occurs. {@link WriteStateException#isDirty()} will always return * false, because new index state file is not yet referenced by manifest file. */ public long writeIndex(String reason, IndexMetadata indexMetadata) throws WriteStateException { final Index index = indexMetadata.getIndex(); logger.trace("[{}] writing state, reason [{}]", index, reason); try { long generation = INDEX_METADATA_FORMAT.write(indexMetadata, nodeEnv.indexPaths(indexMetadata.getIndex())); logger.trace("[{}] state written", index); return generation; } catch (WriteStateException ex) { throw new WriteStateException(false, "[" + index + "]: failed to write index state", ex); } } /** * Writes the global state, *without* the indices states. * * @throws WriteStateException if exception when writing state occurs. {@link WriteStateException#isDirty()} will always return * false, because new global state file is not yet referenced by manifest file. */ long writeGlobalState(String reason, Metadata metadata) throws WriteStateException { logger.trace("[_global] writing state, reason [{}]", reason); try { long generation = METADATA_FORMAT.write(metadata, nodeEnv.nodeDataPaths()); logger.trace("[_global] state written"); return generation; } catch (WriteStateException ex) { throw new WriteStateException(false, "[_global]: failed to write global state", ex); } } /** * Removes old state files in global state directory. * * @param currentGeneration current state generation to keep in the directory. */ void cleanupGlobalState(long currentGeneration) { METADATA_FORMAT.cleanupOldFiles(currentGeneration, nodeEnv.nodeDataPaths()); } /** * Removes old state files in index directory. * * @param index index to perform clean up on. * @param currentGeneration current state generation to keep in the index directory. */ public void cleanupIndex(Index index, long currentGeneration) { INDEX_METADATA_FORMAT.cleanupOldFiles(currentGeneration, nodeEnv.indexPaths(index)); } /** * Creates empty cluster state file on disk, deleting global metadata and unreferencing all index metadata * (only used for dangling indices at that point). */ public void unreferenceAll() throws IOException { MANIFEST_FORMAT.writeAndCleanup(Manifest.empty(), nodeEnv.nodeDataPaths()); // write empty file so that indices become unreferenced METADATA_FORMAT.cleanupOldFiles(Long.MAX_VALUE, nodeEnv.nodeDataPaths()); } /** * Removes manifest file, global metadata and all index metadata */ public void deleteAll() throws IOException { // To ensure that the metadata is never reimported by loadFullStateBWC in case where the deletions here fail mid-way through, // we first write an empty manifest file so that the indices become unreferenced, then clean up the indices, and only then delete // the manifest file. unreferenceAll(); for (String indexFolderName : nodeEnv.availableIndexFolders()) { // delete meta state directories of indices MetadataStateFormat.deleteMetaState(nodeEnv.resolveIndexFolder(indexFolderName)); } MANIFEST_FORMAT.cleanupOldFiles(Long.MAX_VALUE, nodeEnv.nodeDataPaths()); // finally delete manifest } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy