1111import io .javaoperatorsdk .operator .processing .event .ResourceID ;
1212import io .javaoperatorsdk .operator .processing .event .source .CachingEventSource ;
1313
14+ /**
15+ * <p>
16+ * Pols resource (on contrary to {@link PerResourcePollingEventSource}) not per resource bases but
17+ * instead to calls supplier periodically and independently of the number of state of custom
18+ * resources managed by the operator. It is called on start (synced). This means that when the
19+ * reconciler first time executed on startup a poll already happened before. So if the cache does
20+ * not contain the target resource it means it is not created yet or was deleted while an operator
21+ * was not running.
22+ * </p>
23+ * <p>
24+ * Another caveat with this is if the cached object is checked in the reconciler and created since
25+ * not in the cache it should be manually added to the cache, since it can happen that the
26+ * reconciler is triggered before the cache is propagated with the new resource from a scheduled
27+ * execution. See {@link PollingEventSource##put(ResourceID, Object)}.
28+ * </p>
29+ * So the generic workflow in reconciler should be:
30+ *
31+ * <ul>
32+ * <li>Check if the cache contains the resource.</li>
33+ * <li>If cache contains the resource reconcile it - compare with target state, update if necessary
34+ * </li>
35+ * <li>if cache not contains the resource create it.</li>
36+ * <li>If the resource was created or updated, put the new version of the resource manually to the
37+ * cache.</li>
38+ * </ul>
39+ *
40+ * @param <T> type of the polled resource
41+ * @param <P> primary resource type
42+ */
1443public class PollingEventSource <T , P extends HasMetadata > extends CachingEventSource <T , P > {
1544
1645 private static final Logger log = LoggerFactory .getLogger (PollingEventSource .class );
@@ -29,6 +58,7 @@ public PollingEventSource(Supplier<Map<ResourceID, T>> supplier,
2958 @ Override
3059 public void start () throws OperatorException {
3160 super .start ();
61+ getStateAndFillCache ();
3262 timer .schedule (new TimerTask () {
3363 @ Override
3464 public void run () {
@@ -47,6 +77,10 @@ protected void getStateAndFillCache() {
4777 cache .keys ().filter (e -> !values .containsKey (e )).forEach (super ::handleDelete );
4878 }
4979
80+ public void put (ResourceID key , T resource ) {
81+ cache .put (key , resource );
82+ }
83+
5084 @ Override
5185 public void stop () throws OperatorException {
5286 super .stop ();
@@ -61,15 +95,7 @@ public void stop() throws OperatorException {
6195 */
6296 @ Override
6397 public Optional <T > getAssociated (P primary ) {
64- return getValueFromCacheOrSupplier (ResourceID .fromResource (primary ));
98+ return getCachedValue (ResourceID .fromResource (primary ));
6599 }
66100
67- public Optional <T > getValueFromCacheOrSupplier (ResourceID resourceID ) {
68- var resource = getCachedValue (resourceID );
69- if (resource .isPresent ()) {
70- return resource ;
71- }
72- getStateAndFillCache ();
73- return getCachedValue (resourceID );
74- }
75101}
0 commit comments