Skip to content

Commit c709765

Browse files
authored
Add optimized updateSingleProject on Metadata (#129585)
For legacy reasons, `Metadata.Builder` contains a map of `ProjectMetadata.Builder`s instead of a map of `ProjectMetadata`s. While the plan is still to change that, there is a significant amount of refactoring that needs to be done before we can make that change. We have seen regressions in benchmarks due to this extra conversion of project metadata -> builder -> project metadata. We add a method that allows updating a single project without having to convert all the projects to builders first.
1 parent b22bf83 commit c709765

File tree

3 files changed

+54
-4
lines changed

3 files changed

+54
-4
lines changed

server/src/main/java/org/elasticsearch/cluster/ClusterState.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1005,7 +1005,7 @@ public ClusterState copyAndUpdateMetadata(Consumer<Metadata.Builder> updater) {
10051005
}
10061006

10071007
public ClusterState copyAndUpdateProject(ProjectId projectId, Consumer<ProjectMetadata.Builder> updater) {
1008-
return copyAndUpdate(builder -> builder.putProjectMetadata(metadata().getProject(projectId).copyAndUpdate(updater)));
1008+
return copyAndUpdate(builder -> builder.metadata(metadata.copyAndUpdateProject(projectId, updater)));
10091009
}
10101010

10111011
@SuppressForbidden(reason = "directly reading ClusterState#clusterFeatures")

server/src/main/java/org/elasticsearch/cluster/ProjectState.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,14 +73,14 @@ public int hashCode() {
7373
public ClusterState updatedState(Consumer<ProjectMetadata.Builder> projectBuilderConsumer) {
7474
ProjectMetadata.Builder projectBuilder = ProjectMetadata.builder(metadata());
7575
projectBuilderConsumer.accept(projectBuilder);
76-
return ClusterState.builder(cluster).putProjectMetadata(projectBuilder).build();
76+
return updatedState(projectBuilder.build());
7777
}
7878

7979
/**
8080
* Build a new {@link ClusterState} with the updated project.
8181
*/
8282
public ClusterState updatedState(ProjectMetadata updatedProject) {
83-
return ClusterState.builder(cluster).putProjectMetadata(updatedProject).build();
83+
return ClusterState.builder(cluster).metadata(cluster.metadata().withUpdatedProject(updatedProject)).build();
8484
}
8585

8686
/**
@@ -100,6 +100,6 @@ public ProjectState updateProject(ProjectMetadata updatedProject) {
100100
)
101101
);
102102
}
103-
return new ProjectState(ClusterState.builder(cluster).putProjectMetadata(updatedProject).build(), project);
103+
return new ProjectState(updatedState(updatedProject), project);
104104
}
105105
}

server/src/main/java/org/elasticsearch/cluster/metadata/Metadata.java

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,44 @@ private Metadata updateSingleProject(Function<ProjectMetadata, ProjectMetadata>
404404
}
405405
}
406406

407+
/**
408+
* Updates a single project in the metadata. This offers a more performant way of updating a single project compared to the Builder.
409+
*/
410+
@FixForMultiProject // We should reconsider whether this method is valuable once we update Metadata.Builder to hold constructed projects
411+
// instead of project builders.
412+
public Metadata withUpdatedProject(ProjectMetadata updatedProject) {
413+
final var existingProject = projectMetadata.get(updatedProject.id());
414+
if (existingProject == null) {
415+
throw new IllegalArgumentException(
416+
"Can only update existing project, cannot add a new project [" + updatedProject.id() + "]. Use the builder instead"
417+
);
418+
}
419+
if (updatedProject == existingProject) {
420+
return this;
421+
}
422+
final Map<ProjectId, ProjectMetadata> updatedMap;
423+
if (projects().size() == 1) {
424+
updatedMap = Map.of(updatedProject.id(), updatedProject);
425+
} else {
426+
final var hashMap = new HashMap<>(projectMetadata);
427+
hashMap.put(updatedProject.id(), updatedProject);
428+
updatedMap = Collections.unmodifiableMap(hashMap);
429+
}
430+
return new Metadata(
431+
clusterUUID,
432+
clusterUUIDCommitted,
433+
version,
434+
coordinationMetadata,
435+
updatedMap,
436+
transientSettings,
437+
persistentSettings,
438+
settings,
439+
hashesOfConsistentSettings,
440+
customs,
441+
reservedStateMetadata
442+
);
443+
}
444+
407445
public long version() {
408446
return this.version;
409447
}
@@ -1487,6 +1525,18 @@ public Metadata copyAndUpdate(Consumer<Builder> updater) {
14871525
return builder.build();
14881526
}
14891527

1528+
public Metadata copyAndUpdateProject(ProjectId projectId, Consumer<ProjectMetadata.Builder> updater) {
1529+
final var existingProject = projectMetadata.get(projectId);
1530+
if (existingProject == null) {
1531+
throw new IllegalArgumentException(
1532+
"Can only update existing project, cannot add a new project [" + projectId + "]. Use the builder instead"
1533+
);
1534+
}
1535+
final var builder = ProjectMetadata.builder(existingProject);
1536+
updater.accept(builder);
1537+
return withUpdatedProject(builder.build());
1538+
}
1539+
14901540
public static class Builder {
14911541

14921542
private String clusterUUID;

0 commit comments

Comments
 (0)