5050 * selected through {@link Mode}:
5151 *
5252 * <ul>
53- * <li>{@link Options#filterIfOptimisticLocking()} ({@link Mode#FILTER_IF_OPTIMISTIC_LOCKING}) -
54- * the own event is filtered only when the write uses optimistic locking (i.e. the resource
55- * version is set on the resource being written); otherwise the response is only cached. This
56- * is the safe default for plain update/patch operations.
53+ * <li>{@link Options#filterWithOptimisticLocking()} ({@link Mode#FILTER_WITH_OPTIMISTIC_LOCKING})
54+ * - the own event is filtered; the write must use optimistic locking (i.e. a resource version
55+ * is set on the resource being written), otherwise an {@link IllegalArgumentException} is
56+ * thrown. Requiring optimistic locking guarantees that a concurrent third-party change is
57+ * rejected by the API server rather than silently filtered out.
5758 * <li>{@link Options#matchAndFilter(Matcher)} / {@link
5859 * Options#matchAndFilterWithDefaultMatcher(io.javaoperatorsdk.operator.processing.matcher.UpdateType)}
5960 * ({@link Mode#FILTER_IF_NOT_MATCHING}) - before writing, the desired state is compared to
@@ -860,7 +861,7 @@ public P jsonMergePatchPrimaryStatus(P resource, Options options) {
860861 * Low-level building block behind all the update/patch operations above: runs the given {@code
861862 * updateOperation} against the API server, then caches the response and (depending on the {@link
862863 * Options}) filters the resulting own event. The target event source is resolved automatically
863- * from the desired resource's type. Uses {@link Options#filterIfOptimisticLocking ()}.
864+ * from the desired resource's type. Uses {@link Options#filterWithOptimisticLocking ()}.
864865 *
865866 * @param resource the desired resource being written
866867 * @param updateOperation the actual write to perform (update, patch, edit, ...) returning the
@@ -871,7 +872,7 @@ public P jsonMergePatchPrimaryStatus(P resource, Options options) {
871872 * type
872873 */
873874 public <R extends HasMetadata > R resourcePatch (R resource , UnaryOperator <R > updateOperation ) {
874- return resourcePatch (resource , updateOperation , Options .filterIfOptimisticLocking ());
875+ return resourcePatch (resource , updateOperation , Options .filterWithOptimisticLocking ());
875876 }
876877
877878 @ SuppressWarnings ({"rawtypes" , "unchecked" })
@@ -917,7 +918,7 @@ private <R extends HasMetadata> R resourcePatch(
917918 @ Deprecated (forRemoval = true )
918919 public <R extends HasMetadata > R resourcePatch (
919920 R desired , UnaryOperator <R > updateOperation , ManagedInformerEventSource <R , P , ?> ies ) {
920- return resourcePatch (desired , updateOperation , ies , Options .filterIfOptimisticLocking ());
921+ return resourcePatch (desired , updateOperation , ies , Options .filterWithOptimisticLocking ());
921922 }
922923
923924 private <R extends HasMetadata > R resourcePatch (
@@ -947,7 +948,9 @@ private <R extends HasMetadata> R resourcePatch(
947948 * @param <R> the resource type
948949 * @return the resource as returned by {@code updateOperation}, or {@code actualResource} when the
949950 * desired state already matches
950- * @throws IllegalArgumentException if the mode requires a matcher but none is provided
951+ * @throws IllegalArgumentException if the mode requires a matcher but none is provided, or if the
952+ * mode is {@link Mode#FILTER_WITH_OPTIMISTIC_LOCKING} but the resource being written has no
953+ * resource version set
951954 */
952955 // visible for testing
953956 <R extends HasMetadata > R resourcePatch (
@@ -982,10 +985,16 @@ <R extends HasMetadata> R resourcePatch(
982985 return actualResource ;
983986 }
984987
985- boolean optimisticLocking = desiredResource .getMetadata ().getResourceVersion () != null ;
988+ if (options .getMode () == Mode .FILTER_WITH_OPTIMISTIC_LOCKING
989+ && desiredResource .getMetadata ().getResourceVersion () == null ) {
990+ throw new IllegalArgumentException (
991+ "Mode "
992+ + Mode .FILTER_WITH_OPTIMISTIC_LOCKING
993+ + " requires optimistic locking, but no resource version is set on the resource: "
994+ + ResourceID .fromResource (desiredResource ));
995+ }
986996
987- if (options .getMode () == Mode .CACHE_ONLY
988- || (options .getMode () == Mode .FILTER_IF_OPTIMISTIC_LOCKING && !optimisticLocking )) {
997+ if (options .getMode () == Mode .CACHE_ONLY ) {
989998 return ies .updateAndCacheResource (desiredResource , updateOperation );
990999 } else {
9911000 return ies .eventFilteringUpdateAndCacheResource (desiredResource , updateOperation );
@@ -1250,8 +1259,8 @@ public P addFinalizerWithSSA(String finalizerName) {
12501259 * is needed for safe own-event filtering), and the trade-offs of the default matchers.
12511260 *
12521261 * <ul>
1253- * <li>{@link #filterIfOptimisticLocking ()} - filter the own event only when the write uses
1254- * optimistic locking, otherwise only cache the response .
1262+ * <li>{@link #filterWithOptimisticLocking ()} - filter the own event; requires the write to use
1263+ * optimistic locking (a resource version set), throwing if none is set .
12551264 * <li>{@link #matchAndFilter(Matcher)} / {@link #matchAndFilterWithDefaultMatcher(UpdateType)}
12561265 * - skip the write when the desired state already matches the actual state, otherwise write
12571266 * and filter the own event.
@@ -1265,8 +1274,8 @@ public static class Options {
12651274
12661275 private static final Options ALWAYS_FILTER = new Options (Mode .FORCE_FILTER , null );
12671276 private static final Options ONLY_CACHE = new Options (Mode .CACHE_ONLY , null );
1268- private static final Options FILTER_IF_OPTIMISTIC_LOCKING =
1269- new Options (Mode .FILTER_IF_OPTIMISTIC_LOCKING , null );
1277+ private static final Options FILTER_WITH_OPTIMISTIC_LOCKING =
1278+ new Options (Mode .FILTER_WITH_OPTIMISTIC_LOCKING , null );
12701279
12711280 private final Mode mode ;
12721281 private final Matcher matcher ;
@@ -1310,14 +1319,31 @@ public static Options cacheOnly(Matcher matcher) {
13101319 }
13111320
13121321 /**
1313- * Filters the own event only when the write uses optimistic locking (a resource version is set
1314- * on the written resource); otherwise only caches the response. This is the safe default for
1315- * plain update/patch operations.
1322+ * Filters the own event resulting from the write, requiring the write to use optimistic locking
1323+ * (a resource version set on the written resource). If no resource version is set an {@link
1324+ * IllegalArgumentException} is thrown when the operation is performed. Requiring optimistic
1325+ * locking guarantees that a concurrent third-party change is rejected by the API server rather
1326+ * than being silently filtered out.
13161327 *
1317- * @return options that filter the own event only when optimistic locking is used
1328+ * @return options that filter the own event, requiring optimistic locking
1329+ */
1330+ public static Options filterWithOptimisticLocking () {
1331+ return FILTER_WITH_OPTIMISTIC_LOCKING ;
1332+ }
1333+
1334+ /**
1335+ * Like {@link #filterWithOptimisticLocking()} but additionally skips the write when the desired
1336+ * state already matches the actual (cached) state according to the given {@link Matcher}. When
1337+ * a write is needed it still requires optimistic locking (see {@link
1338+ * #filterWithOptimisticLocking()}).
1339+ *
1340+ * @param matcher the matcher used to decide whether the actual state already matches the
1341+ * desired
1342+ * @return options that match using the given matcher and, when a write is needed, filter the
1343+ * own event requiring optimistic locking
13181344 */
1319- public static Options filterIfOptimisticLocking ( ) {
1320- return FILTER_IF_OPTIMISTIC_LOCKING ;
1345+ public static Options filterWithOptimisticLocking ( Matcher matcher ) {
1346+ return new Options ( Mode . FILTER_WITH_OPTIMISTIC_LOCKING , matcher ) ;
13211347 }
13221348
13231349 /**
@@ -1362,14 +1388,14 @@ public boolean requiresMatcher() {
13621388 /**
13631389 * The strategy used to decide how the own event resulting from a write is handled. This is a
13641390 * low-level enum; users should not reference it directly but select the desired behavior through
1365- * the {@link Options} factory methods (e.g. {@link Options#filterIfOptimisticLocking ()}, {@link
1391+ * the {@link Options} factory methods (e.g. {@link Options#filterWithOptimisticLocking ()}, {@link
13661392 * Options#matchAndFilter(Matcher)}, {@link Options#cacheOnly()}, {@link
13671393 * Options#forceFilterEvents()}), which is why each constant links to its corresponding factory.
13681394 */
13691395 @ Experimental (API_MIGHT_CHANGE )
13701396 enum Mode {
1371- /** See {@link Options#filterIfOptimisticLocking ()}. */
1372- FILTER_IF_OPTIMISTIC_LOCKING ,
1397+ /** See {@link Options#filterWithOptimisticLocking ()}. */
1398+ FILTER_WITH_OPTIMISTIC_LOCKING ,
13731399 /** See {@link Options#matchAndFilter(Matcher)}. */
13741400 FILTER_IF_NOT_MATCHING ,
13751401 /** See {@link Options#cacheOnly()}. */
0 commit comments