2020import org .elasticsearch .common .io .stream .StreamInput ;
2121import org .elasticsearch .common .io .stream .StreamOutput ;
2222import org .elasticsearch .common .settings .Settings ;
23+ import org .elasticsearch .common .util .set .Sets ;
2324import org .elasticsearch .xcontent .ToXContent ;
2425
2526import java .io .IOException ;
2627import java .util .Collections ;
28+ import java .util .HashSet ;
2729import java .util .Iterator ;
2830import java .util .Map ;
31+ import java .util .Objects ;
32+ import java .util .Set ;
2933
3034/**
3135 * Represents a registry for managing and retrieving project-specific state in the cluster state.
3236 */
3337public class ProjectStateRegistry extends AbstractNamedDiffable <ClusterState .Custom > implements ClusterState .Custom {
3438 public static final String TYPE = "projects_registry" ;
35- public static final ProjectStateRegistry EMPTY = new ProjectStateRegistry (Collections .emptyMap ());
39+ public static final ProjectStateRegistry EMPTY = new ProjectStateRegistry (Collections .emptyMap (), Collections . emptySet (), 0 );
3640
3741 private final Map <ProjectId , Settings > projectsSettings ;
42+ // Projects that have been marked for deletion based on their file-based setting
43+ private final Set <ProjectId > projectsMarkedForDeletion ;
44+ // A counter that is incremented each time one or more projects are marked for deletion.
45+ private final long projectsMarkedForDeletionGeneration ;
3846
3947 public ProjectStateRegistry (StreamInput in ) throws IOException {
4048 projectsSettings = in .readMap (ProjectId ::readFrom , Settings ::readSettingsFromStream );
49+ if (in .getTransportVersion ().onOrAfter (TransportVersions .PROJECT_STATE_REGISTRY_RECORDS_DELETIONS )) {
50+ projectsMarkedForDeletion = in .readCollectionAsImmutableSet (ProjectId ::readFrom );
51+ projectsMarkedForDeletionGeneration = in .readVLong ();
52+ } else {
53+ projectsMarkedForDeletion = Collections .emptySet ();
54+ projectsMarkedForDeletionGeneration = 0 ;
55+ }
4156 }
4257
43- private ProjectStateRegistry (Map <ProjectId , Settings > projectsSettings ) {
58+ private ProjectStateRegistry (
59+ Map <ProjectId , Settings > projectsSettings ,
60+ Set <ProjectId > projectsMarkedForDeletion ,
61+ long projectsMarkedForDeletionGeneration
62+ ) {
4463 this .projectsSettings = projectsSettings ;
64+ this .projectsMarkedForDeletion = projectsMarkedForDeletion ;
65+ this .projectsMarkedForDeletionGeneration = projectsMarkedForDeletionGeneration ;
4566 }
4667
4768 /**
@@ -72,9 +93,11 @@ public Iterator<? extends ToXContent> toXContentChunked(ToXContent.Params params
7293 builder .startObject ("settings" );
7394 entry .getValue ().toXContent (builder , new ToXContent .MapParams (Collections .singletonMap ("flat_settings" , "true" )));
7495 builder .endObject ();
96+ builder .field ("marked_for_deletion" , projectsMarkedForDeletion .contains (entry .getKey ()));
7597 return builder .endObject ();
7698 }),
77- Iterators .single ((builder , p ) -> builder .endArray ())
99+ Iterators .single ((builder , p ) -> builder .endArray ()),
100+ Iterators .single ((builder , p ) -> builder .field ("projects_marked_for_deletion_generation" , projectsMarkedForDeletionGeneration ))
78101 );
79102 }
80103
@@ -95,12 +118,44 @@ public TransportVersion getMinimalSupportedVersion() {
95118 @ Override
96119 public void writeTo (StreamOutput out ) throws IOException {
97120 out .writeMap (projectsSettings );
121+ if (out .getTransportVersion ().onOrAfter (TransportVersions .PROJECT_STATE_REGISTRY_RECORDS_DELETIONS )) {
122+ out .writeCollection (projectsMarkedForDeletion );
123+ out .writeVLong (projectsMarkedForDeletionGeneration );
124+ } else {
125+ // There should be no deletion unless all MP nodes are at or after PROJECT_STATE_REGISTRY_RECORDS_DELETIONS
126+ assert projectsMarkedForDeletion .isEmpty ();
127+ assert projectsMarkedForDeletionGeneration == 0 ;
128+ }
98129 }
99130
100131 public int size () {
101132 return projectsSettings .size ();
102133 }
103134
135+ public long getProjectsMarkedForDeletionGeneration () {
136+ return projectsMarkedForDeletionGeneration ;
137+ }
138+
139+ // visible for testing
140+ Map <ProjectId , Settings > getProjectsSettings () {
141+ return Collections .unmodifiableMap (projectsSettings );
142+ }
143+
144+ @ Override
145+ public boolean equals (Object o ) {
146+ if (this == o ) return true ;
147+ if (o instanceof ProjectStateRegistry == false ) return false ;
148+ ProjectStateRegistry that = (ProjectStateRegistry ) o ;
149+ return projectsMarkedForDeletionGeneration == that .projectsMarkedForDeletionGeneration
150+ && Objects .equals (projectsSettings , that .projectsSettings )
151+ && Objects .equals (projectsMarkedForDeletion , that .projectsMarkedForDeletion );
152+ }
153+
154+ @ Override
155+ public int hashCode () {
156+ return Objects .hash (projectsSettings , projectsMarkedForDeletion , projectsMarkedForDeletionGeneration );
157+ }
158+
104159 public static Builder builder (ClusterState original ) {
105160 ProjectStateRegistry projectRegistry = original .custom (TYPE , EMPTY );
106161 return builder (projectRegistry );
@@ -116,22 +171,46 @@ public static Builder builder() {
116171
117172 public static class Builder {
118173 private final ImmutableOpenMap .Builder <ProjectId , Settings > projectsSettings ;
174+ private final Set <ProjectId > projectsMarkedForDeletion ;
175+ private final long projectsMarkedForDeletionGeneration ;
176+ private boolean newProjectMarkedForDeletion = false ;
119177
120178 private Builder () {
121179 this .projectsSettings = ImmutableOpenMap .builder ();
180+ projectsMarkedForDeletion = new HashSet <>();
181+ projectsMarkedForDeletionGeneration = 0 ;
122182 }
123183
124184 private Builder (ProjectStateRegistry original ) {
125185 this .projectsSettings = ImmutableOpenMap .builder (original .projectsSettings );
186+ this .projectsMarkedForDeletion = new HashSet <>(original .projectsMarkedForDeletion );
187+ this .projectsMarkedForDeletionGeneration = original .projectsMarkedForDeletionGeneration ;
126188 }
127189
128190 public Builder putProjectSettings (ProjectId projectId , Settings settings ) {
129191 projectsSettings .put (projectId , settings );
130192 return this ;
131193 }
132194
195+ public Builder markProjectForDeletion (ProjectId projectId ) {
196+ if (projectsMarkedForDeletion .add (projectId )) {
197+ newProjectMarkedForDeletion = true ;
198+ }
199+ return this ;
200+ }
201+
133202 public ProjectStateRegistry build () {
134- return new ProjectStateRegistry (projectsSettings .build ());
203+ final var unknownButUnderDeletion = Sets .difference (projectsMarkedForDeletion , projectsSettings .keys ());
204+ if (unknownButUnderDeletion .isEmpty () == false ) {
205+ throw new IllegalArgumentException (
206+ "Cannot mark projects for deletion that are not in the registry: " + unknownButUnderDeletion
207+ );
208+ }
209+ return new ProjectStateRegistry (
210+ projectsSettings .build (),
211+ projectsMarkedForDeletion ,
212+ newProjectMarkedForDeletion ? projectsMarkedForDeletionGeneration + 1 : projectsMarkedForDeletionGeneration
213+ );
135214 }
136215 }
137216}
0 commit comments