diff --git a/server/src/main/java/org/elasticsearch/cluster/ClusterState.java b/server/src/main/java/org/elasticsearch/cluster/ClusterState.java index fdc74d6e354c5..f4b10b3a77c4c 100644 --- a/server/src/main/java/org/elasticsearch/cluster/ClusterState.java +++ b/server/src/main/java/org/elasticsearch/cluster/ClusterState.java @@ -1005,7 +1005,7 @@ public ClusterState copyAndUpdateMetadata(Consumer updater) { } public ClusterState copyAndUpdateProject(ProjectId projectId, Consumer updater) { - return copyAndUpdate(builder -> builder.putProjectMetadata(metadata().getProject(projectId).copyAndUpdate(updater))); + return copyAndUpdate(builder -> builder.metadata(metadata.copyAndUpdateProject(projectId, updater))); } @SuppressForbidden(reason = "directly reading ClusterState#clusterFeatures") diff --git a/server/src/main/java/org/elasticsearch/cluster/ProjectState.java b/server/src/main/java/org/elasticsearch/cluster/ProjectState.java index 7e78ddf5bcbf1..07dcae3ae575c 100644 --- a/server/src/main/java/org/elasticsearch/cluster/ProjectState.java +++ b/server/src/main/java/org/elasticsearch/cluster/ProjectState.java @@ -73,14 +73,14 @@ public int hashCode() { public ClusterState updatedState(Consumer projectBuilderConsumer) { ProjectMetadata.Builder projectBuilder = ProjectMetadata.builder(metadata()); projectBuilderConsumer.accept(projectBuilder); - return ClusterState.builder(cluster).putProjectMetadata(projectBuilder).build(); + return updatedState(projectBuilder.build()); } /** * Build a new {@link ClusterState} with the updated project. */ public ClusterState updatedState(ProjectMetadata updatedProject) { - return ClusterState.builder(cluster).putProjectMetadata(updatedProject).build(); + return ClusterState.builder(cluster).metadata(cluster.metadata().withUpdatedProject(updatedProject)).build(); } /** @@ -100,6 +100,6 @@ public ProjectState updateProject(ProjectMetadata updatedProject) { ) ); } - return new ProjectState(ClusterState.builder(cluster).putProjectMetadata(updatedProject).build(), project); + return new ProjectState(updatedState(updatedProject), project); } } diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/Metadata.java b/server/src/main/java/org/elasticsearch/cluster/metadata/Metadata.java index 5309bff27f8c0..2b024970fcddd 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/Metadata.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/Metadata.java @@ -404,6 +404,44 @@ private Metadata updateSingleProject(Function } } + /** + * Updates a single project in the metadata. This offers a more performant way of updating a single project compared to the Builder. + */ + @FixForMultiProject // We should reconsider whether this method is valuable once we update Metadata.Builder to hold constructed projects + // instead of project builders. + public Metadata withUpdatedProject(ProjectMetadata updatedProject) { + final var existingProject = projectMetadata.get(updatedProject.id()); + if (existingProject == null) { + throw new IllegalArgumentException( + "Can only update existing project, cannot add a new project [" + updatedProject.id() + "]. Use the builder instead" + ); + } + if (updatedProject == existingProject) { + return this; + } + final Map updatedMap; + if (projects().size() == 1) { + updatedMap = Map.of(updatedProject.id(), updatedProject); + } else { + final var hashMap = new HashMap<>(projectMetadata); + hashMap.put(updatedProject.id(), updatedProject); + updatedMap = Collections.unmodifiableMap(hashMap); + } + return new Metadata( + clusterUUID, + clusterUUIDCommitted, + version, + coordinationMetadata, + updatedMap, + transientSettings, + persistentSettings, + settings, + hashesOfConsistentSettings, + customs, + reservedStateMetadata + ); + } + public long version() { return this.version; } @@ -1487,6 +1525,18 @@ public Metadata copyAndUpdate(Consumer updater) { return builder.build(); } + public Metadata copyAndUpdateProject(ProjectId projectId, Consumer updater) { + final var existingProject = projectMetadata.get(projectId); + if (existingProject == null) { + throw new IllegalArgumentException( + "Can only update existing project, cannot add a new project [" + projectId + "]. Use the builder instead" + ); + } + final var builder = ProjectMetadata.builder(existingProject); + updater.accept(builder); + return withUpdatedProject(builder.build()); + } + public static class Builder { private String clusterUUID;