Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
e30fd2d
draft
JVerwolf Mar 12, 2025
62bb968
Update docs/changelog/124651.yaml
JVerwolf Mar 12, 2025
ba59b56
[CI] Auto commit changes from spotless
Mar 12, 2025
559e4d5
Fix metadata system datastream delete service
JVerwolf Mar 12, 2025
ec21ae0
Spotless
JVerwolf Mar 12, 2025
0019d10
Merge branch 'bugfix/datastreams-should-be-restorable' of github.com:…
JVerwolf Mar 12, 2025
d77f0c6
Merge branch 'main' of github.com:elastic/elasticsearch into bugfix/d…
JVerwolf Mar 12, 2025
5379635
Fix merge error
JVerwolf Mar 12, 2025
7d19d6f
Clean up comments
JVerwolf Mar 12, 2025
c1a490a
Ensure only system datastreams are deleted
JVerwolf Mar 12, 2025
1b0ce59
Add test for deleted system datastreams
JVerwolf Mar 12, 2025
530d06f
Update changelog summary for PR 124651
JVerwolf Mar 12, 2025
8df4410
Change exception
JVerwolf Mar 12, 2025
471b7c4
Merge branch 'bugfix/datastreams-should-be-restorable' of github.com:…
JVerwolf Mar 12, 2025
1cd7cdd
address feedback and update transport action
JVerwolf Mar 12, 2025
527e1f3
remove wip
JVerwolf Mar 12, 2025
165957a
Add tests
JVerwolf Mar 12, 2025
6553cf6
Update docs
JVerwolf Mar 12, 2025
4338727
Update server/src/main/java/org/elasticsearch/cluster/metadata/Metada…
JVerwolf Mar 13, 2025
fd5dc2d
PR feedback: remove stream from clusterstate update
JVerwolf Mar 13, 2025
27f7696
PR feedback: remove overloaded function for clusterstate
JVerwolf Mar 13, 2025
f9a3d97
PR feedback: remove duplicate validation
JVerwolf Mar 13, 2025
c8cc652
PR feedback: ensure system datastream alaises are restored
JVerwolf Mar 13, 2025
2199e69
PR feedback: remove Metadata#projectFor
JVerwolf Mar 13, 2025
248c101
PR feedback: update testDeleteMissing and throw ResourceNotFoundExcep…
JVerwolf Mar 13, 2025
7fd3a13
Fix alias restore
JVerwolf Mar 13, 2025
b7c760c
PR feedback: move exception further up
JVerwolf Mar 13, 2025
cf0b3b4
PR feedback: add partial snapshot test
JVerwolf Mar 13, 2025
1d35ff5
spotless
JVerwolf Mar 13, 2025
3bbd02a
Merge branch 'main' of github.com:elastic/elasticsearch into bugfix/d…
JVerwolf Mar 13, 2025
4d4fd55
Remove unused test helper
JVerwolf Mar 13, 2025
09e2b87
Remove print line
JVerwolf Mar 13, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions docs/changelog/124651.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pr: 124651
summary: "Bug Fix: System Data Streams Should Be Restorable"
area: Infra/Core
type: bug
issues: []
1 change: 1 addition & 0 deletions server/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ dependencies {
}
internalClusterTestImplementation(project(':modules:reindex'))
internalClusterTestImplementation(project(':modules:mapper-extras'))
internalClusterTestImplementation(project(':modules:data-streams'))
}

spotless {
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -1865,6 +1865,19 @@ public Optional<ProjectMetadata> lookupProject(Index index) {
return getProjectLookup().project(index);
}

/**
* Attempt to find a project for the supplied {@link DataStream}.
*/
@FixForMultiProject
public Optional<ProjectMetadata> lookupProject(DataStream dataStream) {
// Assumes single project for now.
if (getProject().dataStreams().containsKey(dataStream.getName())) {
return Optional.of(getProject());
} else {
return Optional.empty();
}
}

/**
* Attempt to find a project for the supplied {@link Index}.
* @throws org.elasticsearch.index.IndexNotFoundException if the index does not exist in any project
Expand All @@ -1875,6 +1888,16 @@ public ProjectMetadata projectFor(Index index) {
);
}

/**
* Attempt to find a project for the supplied {@link DataStream}.
* @throws org.elasticsearch.index.IndexNotFoundException if the data stream does not exist in any project
*/
public ProjectMetadata projectFor(DataStream dataStream) {
return lookupProject(dataStream).orElseThrow(
() -> new IndexNotFoundException("dataStream [" + dataStream + "] does not exist in any project")
);
}

/**
* Attempt to find the IndexMetadata for the supplied {@link Index}.
* @throws org.elasticsearch.index.IndexNotFoundException if the index does not exist in any project
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

package org.elasticsearch.cluster.metadata;

import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.ProjectState;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.core.FixForMultiProject;
import org.elasticsearch.index.Index;
import org.elasticsearch.logging.LogManager;
import org.elasticsearch.logging.Logger;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

public class MetadataDeleteDataStreamService {

private static final Logger LOGGER = LogManager.getLogger(MetadataDeleteDataStreamService.class);

/**
* Removes the given data streams from the Cluster State.
*
* @param clusterState The cluster state
* @param dataStreams The data streams to remove
* @param settings The settings
* @return The updated Cluster State
*/
@FixForMultiProject // Once callers have Project State we can update/remove this method.
public static ClusterState deleteDataStreams(ClusterState clusterState, Set<DataStream> dataStreams, Settings settings) {
final Map<ProjectId, Set<DataStream>> byProject = new HashMap<>();
for (DataStream dataStream : dataStreams) {
final ProjectMetadata project = clusterState.metadata().projectFor(dataStream);
byProject.computeIfAbsent(project.id(), ignore -> new HashSet<>()).add(dataStream);
}

for (final Map.Entry<ProjectId, Set<DataStream>> entry : byProject.entrySet()) {
clusterState = deleteDataStream(clusterState.projectState(entry.getKey()), entry.getValue(), settings);
}
return clusterState;
}

/**
* Removes the given data streams from the Project State.
*
* @param projectState The project state
* @param dataStreams The data streams to remove
* @param settings The settings
* @return The updated Project State
*/
public static ClusterState deleteDataStream(ProjectState projectState, Set<DataStream> dataStreams, Settings settings) {
if (dataStreams.isEmpty()) {
return projectState.cluster();
}

Set<Index> backingIndicesToRemove = new HashSet<>();
for (DataStream dataStream : dataStreams) {
assert dataStream != null;
backingIndicesToRemove.addAll(dataStream.getIndices());
backingIndicesToRemove.addAll(dataStream.getFailureIndices());
}

// first delete the data streams and then the indices:
// (this to avoid data stream validation from failing when deleting an index that is part of a data stream
// without updating the data stream)
// TODO: change order when "delete index api" also updates the data stream the "index to be removed" is a member of
ClusterState newState = projectState.updatedState(builder -> {
dataStreams.stream().map(DataStream::getName).forEach(ds -> {
LOGGER.info("removing data stream [{}]", ds);
builder.removeDataStream(ds);
});
});
return MetadataDeleteIndexService.deleteIndices(newState.projectState(projectState.projectId()), backingIndicesToRemove, settings);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import org.elasticsearch.cluster.metadata.MappingMetadata;
import org.elasticsearch.cluster.metadata.Metadata;
import org.elasticsearch.cluster.metadata.MetadataCreateIndexService;
import org.elasticsearch.cluster.metadata.MetadataDeleteDataStreamService;
import org.elasticsearch.cluster.metadata.MetadataDeleteIndexService;
import org.elasticsearch.cluster.metadata.MetadataIndexStateService;
import org.elasticsearch.cluster.metadata.ProjectMetadata;
Expand Down Expand Up @@ -765,6 +766,21 @@ private Set<Index> resolveSystemIndicesToDelete(ClusterState currentState, Set<S
.collect(Collectors.toUnmodifiableSet());
}

/**
* This method determines what system data streams to delete prior to restoring the snapshot. If a datastream in the
* snapshot is not present in the current cluster state, it will be ignored.
*/
private Set<DataStream> resolveSystemDataStreamsToDelete(ClusterState currentState, Collection<DataStream> dataStreamsToRestore) {
if (dataStreamsToRestore == null) {
return Collections.emptySet();
}

return dataStreamsToRestore.stream()
.filter(Objects::nonNull) // Features that aren't present on this node will be warned about in `getFeatureStatesToRestore`
.filter(dataStream -> currentState.metadata().getProject().dataStreams().get(dataStream.getName()) != null)
.collect(Collectors.toUnmodifiableSet());
}

// visible for testing
static DataStream updateDataStream(DataStream dataStream, Metadata.Builder metadata, RestoreSnapshotRequest request) {
String dataStreamName = dataStream.getName();
Expand Down Expand Up @@ -1336,6 +1352,13 @@ public ClusterState execute(ClusterState currentState) {
settings
);

// Clear out all existing system data streams
currentState = MetadataDeleteDataStreamService.deleteDataStreams(
currentState,
resolveSystemDataStreamsToDelete(currentState, dataStreamsToRestore),
settings
);

// List of searchable snapshots indices to restore
final Set<Index> searchableSnapshotsIndices = new HashSet<>();

Expand Down