Clean up singleton-service implementation internals 47/109247/2
authorRobert Varga <robert.varga@pantheon.tech>
Sat, 9 Dec 2023 08:13:28 +0000 (09:13 +0100)
committerRobert Varga <robert.varga@pantheon.tech>
Sat, 9 Dec 2023 08:15:38 +0000 (09:15 +0100)
We have {Active,Placeholder}ServiceGroup, to keep the naming short and
sweet. Also seal ServiceGroup.

JIRA: MDSAL-843
Change-Id: I685ae82e34cf18e603db6e1f51054516caa3d40e
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
singleton-service/mdsal-singleton-dom-impl/src/main/java/org/opendaylight/mdsal/singleton/dom/impl/ActiveServiceGroup.java [moved from singleton-service/mdsal-singleton-dom-impl/src/main/java/org/opendaylight/mdsal/singleton/dom/impl/ClusterSingletonServiceGroupImpl.java with 95% similarity]
singleton-service/mdsal-singleton-dom-impl/src/main/java/org/opendaylight/mdsal/singleton/dom/impl/EOSClusterSingletonServiceProvider.java
singleton-service/mdsal-singleton-dom-impl/src/main/java/org/opendaylight/mdsal/singleton/dom/impl/PlaceholderServiceGroup.java [moved from singleton-service/mdsal-singleton-dom-impl/src/main/java/org/opendaylight/mdsal/singleton/dom/impl/PlaceholderGroup.java with 87% similarity]
singleton-service/mdsal-singleton-dom-impl/src/main/java/org/opendaylight/mdsal/singleton/dom/impl/ServiceGroup.java [moved from singleton-service/mdsal-singleton-dom-impl/src/main/java/org/opendaylight/mdsal/singleton/dom/impl/ClusterSingletonServiceGroup.java with 92% similarity]
singleton-service/mdsal-singleton-dom-impl/src/main/java/org/opendaylight/mdsal/singleton/dom/impl/ServiceInfo.java
singleton-service/mdsal-singleton-dom-impl/src/test/java/org/opendaylight/mdsal/singleton/dom/impl/ActiveServiceGroupTest.java [moved from singleton-service/mdsal-singleton-dom-impl/src/test/java/org/opendaylight/mdsal/singleton/dom/impl/ClusterSingletonServiceGroupImplTest.java with 96% similarity]

@@ -42,29 +42,24 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
- * Implementation of {@link ClusterSingletonServiceGroup} on top of the Entity Ownership Service. Since EOS is atomic
+ * Implementation of {@link ServiceGroup} on top of the Entity Ownership Service. Since EOS is atomic
  * in its operation and singleton services incur startup and most notably cleanup, we need to do something smart here.
  *
  * <p>
  * The implementation takes advantage of the fact that EOS provides stable ownership, i.e. owners are not moved as
  * a result on new candidates appearing. We use two entities:
- * - service entity, to which all nodes register
- * - cleanup entity, which only the service entity owner registers to
+ * <ol>
+ *   <li>service entity, to which all nodes register</li>
+ *   <li>cleanup entity, which only the service entity owner registers to</li>
+ * </ol>
  *
  * <p>
  * Once the cleanup entity ownership is acquired, services are started. As long as the cleanup entity is registered,
  * it should remain the owner. In case a new service owner emerges, the old owner will start the cleanup process,
  * eventually releasing the cleanup entity. The new owner registers for the cleanup entity -- but will not see it
  * granted until the old owner finishes the cleanup.
- *
- * @param <P> the instance identifier path type
- * @param <E> the GenericEntity type
- * @param <C> the GenericEntityOwnershipChange type
- * @param <G> the GenericEntityOwnershipListener type
- * @param <S> the GenericEntityOwnershipService type
  */
-// FIXME: rename to ActiveServiceGroup
-final class ClusterSingletonServiceGroupImpl extends ClusterSingletonServiceGroup {
+final class ActiveServiceGroup extends ServiceGroup {
 
     private enum EntityState {
         /**
@@ -103,7 +98,7 @@ final class ClusterSingletonServiceGroupImpl extends ClusterSingletonServiceGrou
         STOPPING,
     }
 
-    private static final Logger LOG = LoggerFactory.getLogger(ClusterSingletonServiceGroupImpl.class);
+    private static final Logger LOG = LoggerFactory.getLogger(ActiveServiceGroup.class);
 
     private final DOMEntityOwnershipService entityOwnershipService;
     private final String identifier;
@@ -117,13 +112,13 @@ final class ClusterSingletonServiceGroupImpl extends ClusterSingletonServiceGrou
     private final Map<ServiceRegistration, ServiceInfo> services = new HashMap<>();
 
     // Marker for when any state changed
-    private static final AtomicIntegerFieldUpdater<ClusterSingletonServiceGroupImpl> DIRTY_UPDATER =
-            AtomicIntegerFieldUpdater.newUpdater(ClusterSingletonServiceGroupImpl.class, "dirty");
+    private static final AtomicIntegerFieldUpdater<ActiveServiceGroup> DIRTY_UPDATER =
+            AtomicIntegerFieldUpdater.newUpdater(ActiveServiceGroup.class, "dirty");
     private volatile int dirty;
 
     // Simplified lock: non-reentrant, support tryLock() only
-    private static final AtomicIntegerFieldUpdater<ClusterSingletonServiceGroupImpl> LOCK_UPDATER =
-            AtomicIntegerFieldUpdater.newUpdater(ClusterSingletonServiceGroupImpl.class, "lock");
+    private static final AtomicIntegerFieldUpdater<ActiveServiceGroup> LOCK_UPDATER =
+            AtomicIntegerFieldUpdater.newUpdater(ActiveServiceGroup.class, "lock");
     @SuppressWarnings("unused")
     private volatile int lock;
 
@@ -186,7 +181,7 @@ final class ClusterSingletonServiceGroupImpl extends ClusterSingletonServiceGrou
      * @param parent parent service
      * @param services Services list
      */
-    ClusterSingletonServiceGroupImpl(final String identifier, final DOMEntityOwnershipService entityOwnershipService,
+    ActiveServiceGroup(final String identifier, final DOMEntityOwnershipService entityOwnershipService,
             final DOMEntity serviceEntity, final DOMEntity cleanupEntity, final List<ServiceRegistration> services) {
         checkArgument(!identifier.isEmpty(), "Identifier may not be empty");
         this.identifier = identifier;
@@ -199,7 +194,7 @@ final class ClusterSingletonServiceGroupImpl extends ClusterSingletonServiceGrou
     }
 
     @VisibleForTesting
-    ClusterSingletonServiceGroupImpl(final String identifier, final DOMEntity serviceEntity,
+    ActiveServiceGroup(final String identifier, final DOMEntity serviceEntity,
             final DOMEntity cleanupEntity, final DOMEntityOwnershipService entityOwnershipService) {
         this(identifier, entityOwnershipService, serviceEntity, cleanupEntity, ImmutableList.of());
     }
index 042653410e637b58568bc4278dc40e66387af836..c1c31b809596b8df6131b81c7545b9418485175e 100644 (file)
@@ -56,7 +56,7 @@ public final class EOSClusterSingletonServiceProvider
     @VisibleForTesting
     static final @NonNull String CLOSE_SERVICE_ENTITY_TYPE = "org.opendaylight.mdsal.AsyncServiceCloseEntityType";
 
-    private final ConcurrentMap<String, ClusterSingletonServiceGroup> serviceGroupMap = new ConcurrentHashMap<>();
+    private final ConcurrentMap<String, ServiceGroup> serviceGroupMap = new ConcurrentHashMap<>();
     private final DOMEntityOwnershipService entityOwnershipService;
 
     /* EOS Entity Listeners Registration */
@@ -86,7 +86,7 @@ public final class EOSClusterSingletonServiceProvider
         serviceEntityListenerReg = null;
 
         final var future = Futures.allAsList(serviceGroupMap.values().stream()
-            .map(ClusterSingletonServiceGroup::closeClusterSingletonGroup)
+            .map(ServiceGroup::closeClusterSingletonGroup)
             .toList());
         try {
             LOG.debug("Waiting for service groups to stop");
@@ -108,7 +108,7 @@ public final class EOSClusterSingletonServiceProvider
         checkArgument(!Strings.isNullOrEmpty(serviceIdentifier),
             "ClusterSingletonService identifier may not be null nor empty");
 
-        final ClusterSingletonServiceGroup serviceGroup;
+        final ServiceGroup serviceGroup;
         final var existing = serviceGroupMap.get(serviceIdentifier);
         if (existing == null) {
             serviceGroup = createGroup(serviceIdentifier, new ArrayList<>(1));
@@ -136,14 +136,14 @@ public final class EOSClusterSingletonServiceProvider
         return reg;
     }
 
-    private ClusterSingletonServiceGroup createGroup(final String serviceIdentifier,
+    private ServiceGroup createGroup(final String serviceIdentifier,
             final List<ServiceRegistration> services) {
-        return new ClusterSingletonServiceGroupImpl(serviceIdentifier, entityOwnershipService,
+        return new ActiveServiceGroup(serviceIdentifier, entityOwnershipService,
             createEntity(SERVICE_ENTITY_TYPE, serviceIdentifier),
             createEntity(CLOSE_SERVICE_ENTITY_TYPE, serviceIdentifier), services);
     }
 
-    private void initializeOrRemoveGroup(final ClusterSingletonServiceGroup group)
+    private void initializeOrRemoveGroup(final ServiceGroup group)
             throws CandidateAlreadyRegisteredException {
         try {
             group.initialize();
@@ -154,7 +154,7 @@ public final class EOSClusterSingletonServiceProvider
     }
 
     private void removeRegistration(final String serviceIdentifier, final ServiceRegistration reg) {
-        final PlaceholderGroup placeHolder;
+        final PlaceholderServiceGroup placeHolder;
         final ListenableFuture<?> future;
         synchronized (this) {
             final var lookup = verifyNotNull(serviceGroupMap.get(serviceIdentifier));
@@ -165,7 +165,7 @@ public final class EOSClusterSingletonServiceProvider
 
             // Close the group and replace it with a placeholder
             LOG.debug("Closing service group {}", serviceIdentifier);
-            placeHolder = new PlaceholderGroup(lookup, future);
+            placeHolder = new PlaceholderServiceGroup(lookup, future);
 
             final String identifier = reg.getInstance().getIdentifier().getName();
             verify(serviceGroupMap.replace(identifier, lookup, placeHolder));
@@ -177,7 +177,7 @@ public final class EOSClusterSingletonServiceProvider
         future.addListener(() -> finishShutdown(placeHolder), MoreExecutors.directExecutor());
     }
 
-    private synchronized void finishShutdown(final PlaceholderGroup placeHolder) {
+    private synchronized void finishShutdown(final PlaceholderServiceGroup placeHolder) {
         final var identifier = placeHolder.getIdentifier();
         LOG.debug("Service group {} closed", identifier);
 
@@ -23,19 +23,18 @@ import org.slf4j.LoggerFactory;
 
 /**
  * Intermediate place-holder to catch user requests while asynchronous shutdown of previous incarnation of
- * a {@link ClusterSingletonServiceGroup} finishes.
+ * a {@link ServiceGroup} finishes.
  */
-// FIXME: rename to PlaceholderServiceGroup
-final class PlaceholderGroup extends ClusterSingletonServiceGroup {
-    private static final Logger LOG = LoggerFactory.getLogger(PlaceholderGroup.class);
+final class PlaceholderServiceGroup extends ServiceGroup {
+    private static final Logger LOG = LoggerFactory.getLogger(PlaceholderServiceGroup.class);
 
     private final List<ServiceRegistration> services = new ArrayList<>(0);
-    private final ClusterSingletonServiceGroup previous;
+    private final ServiceGroup previous;
     private final ListenableFuture<?> closeFuture;
 
-    private volatile ClusterSingletonServiceGroup successor;
+    private volatile ServiceGroup successor;
 
-    PlaceholderGroup(final ClusterSingletonServiceGroup previous, final ListenableFuture<?> closeFuture) {
+    PlaceholderServiceGroup(final ServiceGroup previous, final ListenableFuture<?> closeFuture) {
         this.previous = requireNonNull(previous);
         this.closeFuture = requireNonNull(closeFuture);
     }
@@ -85,7 +84,7 @@ final class PlaceholderGroup extends ClusterSingletonServiceGroup {
         return services;
     }
 
-    void setSuccessor(final ClusterSingletonServiceGroup successor) {
+    void setSuccessor(final ServiceGroup successor) {
         verifyNoSuccessor();
         this.successor = verifyNotNull(successor);
         LOG.debug("{}: successor set to {}", this, successor);
@@ -16,19 +16,16 @@ import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonService;
 import org.opendaylight.yangtools.concepts.Identifiable;
 
 /**
- * {@link ClusterSingletonServiceGroup} maintains a group of {@link ClusterSingletonService} instances.
+ * {@link ServiceGroup} maintains a group of {@link ClusterSingletonService} instances.
  * All EntityOwnershipChange notifications have to applied to all registered services at the same time in the same
  * manner. All registered services have only one instantiated service instance in a cluster at one time on same
  * Cluster Node. This is realized via a double candidate approach where a service group instance maintains a candidate
  * registration for ownership of the service entity in the cluster and also a registration that acts as a guard to
  * ensure a service group instance has fully closed prior to relinquishing service ownership. To achieve ownership
  * of the service group, a service group candidate must hold ownership of both these entities.
- *
- * @param <P> the instance identifier path type
- * @param <E> the GenericEntity type
  */
-// FIXME: rename to ServiceGroup and seal
-abstract class ClusterSingletonServiceGroup implements Identifiable<String> {
+// FIXME: seal
+abstract class ServiceGroup implements Identifiable<String> {
     /**
      * This method must be called once on startup to initialize this group and register the relevant group entity
      * candidate. It means create relevant Group Entity Candidate Registration.
index 081e5646be680b09a08acbafa5faaf0b0757096a..f62fb6d2e553213bbc34dce1d853c1d32aacdbea 100644 (file)
@@ -14,7 +14,7 @@ import static java.util.Objects.requireNonNull;
 import com.google.common.util.concurrent.ListenableFuture;
 import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jdt.annotation.Nullable;
-import org.opendaylight.mdsal.singleton.dom.impl.ClusterSingletonServiceGroupImpl.ServiceState;
+import org.opendaylight.mdsal.singleton.dom.impl.ActiveServiceGroup.ServiceState;
 
 final class ServiceInfo {
     static final @NonNull ServiceInfo STARTED = new ServiceInfo(ServiceState.STARTED, null);
@@ -42,10 +42,10 @@ import org.opendaylight.mdsal.singleton.common.api.ServiceGroupIdentifier;
 import org.opendaylight.yangtools.concepts.Registration;
 
 /**
- * Testing {@link ClusterSingletonServiceGroupImpl}.
+ * Testing {@link ActiveServiceGroup}.
  */
 @RunWith(MockitoJUnitRunner.StrictStubs.class)
-public class ClusterSingletonServiceGroupImplTest {
+public class ActiveServiceGroupTest {
     public static final String SERVICE_IDENTIFIER = "TestServiceIdent";
     public static final ServiceGroupIdentifier SERVICE_GROUP_IDENT = ServiceGroupIdentifier.create(SERVICE_IDENTIFIER);
 
@@ -65,10 +65,9 @@ public class ClusterSingletonServiceGroupImplTest {
     @Mock
     public DOMEntityOwnershipService mockEosService;
 
-    public ClusterSingletonServiceGroupImpl singletonServiceGroup;
-
-    public ServiceRegistration firstReg;
-    public ServiceRegistration secondReg;
+    private ActiveServiceGroup singletonServiceGroup;
+    private ServiceRegistration firstReg;
+    private ServiceRegistration secondReg;
 
     /**
      * Initialization functionality for every Tests in this suite.
@@ -100,8 +99,7 @@ public class ClusterSingletonServiceGroupImplTest {
             }
         };
 
-        singletonServiceGroup = new ClusterSingletonServiceGroupImpl(SERVICE_IDENTIFIER, MAIN_ENTITY, CLOSE_ENTITY,
-            mockEosService);
+        singletonServiceGroup = new ActiveServiceGroup(SERVICE_IDENTIFIER, MAIN_ENTITY, CLOSE_ENTITY, mockEosService);
     }
 
     /**
@@ -110,7 +108,7 @@ public class ClusterSingletonServiceGroupImplTest {
     @Test
     public void instantiationClusterSingletonServiceGroupNullIdentTest() {
         assertThrows(NullPointerException.class,
-            () -> new ClusterSingletonServiceGroupImpl(null, MAIN_ENTITY, CLOSE_ENTITY, mockEosService));
+            () -> new ActiveServiceGroup(null, MAIN_ENTITY, CLOSE_ENTITY, mockEosService));
     }
 
     /**
@@ -119,7 +117,7 @@ public class ClusterSingletonServiceGroupImplTest {
     @Test
     public void instantiationClusterSingletonServiceGroupEmptyIdentTest() {
         assertThrows(IllegalArgumentException.class,
-            () -> new ClusterSingletonServiceGroupImpl("", MAIN_ENTITY, CLOSE_ENTITY, mockEosService));
+            () -> new ActiveServiceGroup("", MAIN_ENTITY, CLOSE_ENTITY, mockEosService));
     }
 
     /**
@@ -128,7 +126,7 @@ public class ClusterSingletonServiceGroupImplTest {
     @Test
     public void instantiationClusterSingletonServiceGroupNullMainEntityTest() {
         assertThrows(NullPointerException.class,
-            () -> new ClusterSingletonServiceGroupImpl(SERVICE_IDENTIFIER, null, CLOSE_ENTITY, mockEosService));
+            () -> new ActiveServiceGroup(SERVICE_IDENTIFIER, null, CLOSE_ENTITY, mockEosService));
     }
 
     /**
@@ -137,7 +135,7 @@ public class ClusterSingletonServiceGroupImplTest {
     @Test
     public void instantiationClusterSingletonServiceGroupNullCloseEntityTest() {
         assertThrows(NullPointerException.class,
-            () -> new ClusterSingletonServiceGroupImpl(SERVICE_IDENTIFIER, MAIN_ENTITY, null, mockEosService));
+            () -> new ActiveServiceGroup(SERVICE_IDENTIFIER, MAIN_ENTITY, null, mockEosService));
     }
 
     /**
@@ -146,7 +144,7 @@ public class ClusterSingletonServiceGroupImplTest {
     @Test
     public void instantiationClusterSingletonServiceGroupNullEOS_Test() {
         assertThrows(NullPointerException.class,
-            () -> new ClusterSingletonServiceGroupImpl(SERVICE_IDENTIFIER, MAIN_ENTITY, CLOSE_ENTITY, null));
+            () -> new ActiveServiceGroup(SERVICE_IDENTIFIER, MAIN_ENTITY, CLOSE_ENTITY, null));
     }
 
     /**