99
1010package org .elasticsearch .cluster .routing .allocation .decider ;
1111
12+ import org .elasticsearch .TransportVersions ;
1213import org .elasticsearch .common .io .stream .StreamInput ;
1314import org .elasticsearch .common .io .stream .StreamOutput ;
1415import org .elasticsearch .common .io .stream .Writeable ;
@@ -33,6 +34,7 @@ public sealed interface Decision extends ToXContent, Writeable permits Decision.
3334
3435 Single ALWAYS = new Single (Type .YES );
3536 Single YES = new Single (Type .YES );
37+ Single YES_NOT_PREFERRED = new Single (Type .YES , Preferred .NO );
3638 Single NO = new Single (Type .NO );
3739 Single THROTTLE = new Single (Type .THROTTLE );
3840
@@ -48,6 +50,28 @@ static Decision single(Type type, @Nullable String label, @Nullable String expla
4850 return new Single (type , label , explanation , explanationParams );
4951 }
5052
53+ /**
54+ * Creates YES/PREFERRED decision
55+ * @param label label for the Decider that produced this decision
56+ * @param explanation explanation of the decision
57+ * @param explanationParams additional parameters for the decision
58+ * @return new {@link Decision} instance
59+ */
60+ static Decision preferred (@ Nullable String label , @ Nullable String explanation , @ Nullable Object ... explanationParams ) {
61+ return new Single (Type .YES , Preferred .YES , label , explanation , explanationParams );
62+ }
63+
64+ /**
65+ * Creates YES/NOT-PREFERRED decision
66+ * @param label label for the Decider that produced this decision
67+ * @param explanation explanation of the decision
68+ * @param explanationParams additional parameters for the decision
69+ * @return new {@link Decision} instance
70+ */
71+ static Decision notPreferred (@ Nullable String label , @ Nullable String explanation , @ Nullable Object ... explanationParams ) {
72+ return new Single (Type .YES , Preferred .NO , label , explanation , explanationParams );
73+ }
74+
5175 static Decision readFrom (StreamInput in ) throws IOException {
5276 // Determine whether to read a Single or Multi Decision
5377 if (in .readBoolean ()) {
@@ -67,16 +91,20 @@ static Decision readFrom(StreamInput in) throws IOException {
6791
6892 private static Single readSingleFrom (StreamInput in ) throws IOException {
6993 final Type type = Type .readFrom (in );
94+ Preferred preferred = type == Type .YES ? Preferred .YES : Preferred .NO ;
95+ if (in .getTransportVersion ().onOrAfter (TransportVersions .ALLOCATION_DECISION_YES_NOT_PREFERRED )) {
96+ preferred = in .readEnum (Preferred .class );
97+ }
7098 final String label = in .readOptionalString ();
7199 final String explanation = in .readOptionalString ();
72100 if (label == null && explanation == null ) {
73101 return switch (type ) {
74- case YES -> YES ;
102+ case YES -> preferred == Preferred . YES ? YES : YES_NOT_PREFERRED ;
75103 case THROTTLE -> THROTTLE ;
76104 case NO -> NO ;
77105 };
78106 }
79- return new Single (type , label , explanation );
107+ return new Single (type , preferred , label , explanation );
80108 }
81109
82110 /**
@@ -85,6 +113,11 @@ private static Single readSingleFrom(StreamInput in) throws IOException {
85113 */
86114 Type type ();
87115
116+ /**
117+ * @return {@link Preferred}, a soft yes/no
118+ */
119+ Preferred preferred ();
120+
88121 /**
89122 * Get the description label for this decision.
90123 */
@@ -149,10 +182,22 @@ public static Type min(Type a, Type b) {
149182
150183 }
151184
185+ /**
186+ * Preferred indicates if allocation is favorable. It's a soft YES/NO, means allocation is still possible when not-preferred.
187+ */
188+ enum Preferred {
189+ NO ,
190+ YES
191+ }
192+
152193 /**
153194 * Simple class representing a single decision
154195 */
155- record Single (Type type , String label , String explanationString ) implements Decision , ToXContentObject {
196+ record Single (Type type , Preferred preferred , String label , String explanationString ) implements Decision , ToXContentObject {
197+ public Single {
198+ assert type == Type .YES || preferred == Preferred .NO : "only YES type can have preference" ;
199+ }
200+
156201 /**
157202 * Creates a new {@link Single} decision of a given type
158203 * @param type {@link Type} of the decision
@@ -161,6 +206,10 @@ private Single(Type type) {
161206 this (type , null , null , (Object []) null );
162207 }
163208
209+ private Single (Type type , Preferred preferred ) {
210+ this (type , preferred , null , null , (Object []) null );
211+ }
212+
164213 /**
165214 * Creates a new {@link Single} decision of a given type
166215 *
@@ -169,8 +218,27 @@ private Single(Type type) {
169218 * @param explanationParams A set of additional parameters
170219 */
171220 public Single (Type type , @ Nullable String label , @ Nullable String explanation , @ Nullable Object ... explanationParams ) {
221+ this (type , type == Type .YES ? Preferred .YES : Preferred .NO , label , explanation , explanationParams );
222+ }
223+
224+ /**
225+ * Creates a new {@link Single} decision of a given type
226+ *
227+ * @param type {@link Type} of the decision
228+ * @param preferred {@link Preferred} soft yes/no
229+ * @param explanation An explanation of this {@link Decision}
230+ * @param explanationParams A set of additional parameters
231+ */
232+ public Single (
233+ Type type ,
234+ Preferred preferred ,
235+ @ Nullable String label ,
236+ @ Nullable String explanation ,
237+ @ Nullable Object ... explanationParams
238+ ) {
172239 this (
173240 type ,
241+ preferred ,
174242 label ,
175243 explanationParams != null && explanationParams .length > 0
176244 ? String .format (Locale .ROOT , explanation , explanationParams )
@@ -194,10 +262,11 @@ public String getExplanation() {
194262
195263 @ Override
196264 public String toString () {
265+ var preference = type == Type .YES && preferred == Preferred .NO ? "_NOT_PREFERRED" : "" ;
197266 if (explanationString != null ) {
198- return type + "(" + explanationString + ")" ;
267+ return type + preference + "(" + explanationString + ")" ;
199268 }
200- return type + "()" ;
269+ return type + preference + "()" ;
201270 }
202271
203272 @ Override
@@ -214,6 +283,9 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws
214283 public void writeTo (StreamOutput out ) throws IOException {
215284 out .writeBoolean (false ); // flag specifying its a single decision
216285 type .writeTo (out );
286+ if (out .getTransportVersion ().onOrAfter (TransportVersions .ALLOCATION_DECISION_YES_NOT_PREFERRED )) {
287+ out .writeEnum (preferred );
288+ }
217289 out .writeOptionalString (label );
218290 // Flatten explanation on serialization, so that explanationParams
219291 // do not need to be serialized
@@ -255,6 +327,16 @@ public Type type() {
255327 return ret ;
256328 }
257329
330+ @ Override
331+ public Preferred preferred () {
332+ for (var decision : decisions ) {
333+ if (decision .type () != Type .YES || decision .preferred () == Preferred .NO ) {
334+ return Preferred .NO ;
335+ }
336+ }
337+ return Preferred .YES ;
338+ }
339+
258340 @ Override
259341 @ Nullable
260342 public String label () {
0 commit comments