Improve segmented journal actor metrics
[controller.git] / opendaylight / md-sal / eos-dom-akka / src / test / java / org / opendaylight / controller / eos / akka / service / ClusterSingletonIntegrationTest.java
1 /*
2  * Copyright (c) 2021 PANTHEON.tech, s.r.o. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.controller.eos.akka.service;
9
10 import static org.awaitility.Awaitility.await;
11 import static org.junit.Assert.assertFalse;
12 import static org.junit.Assert.assertTrue;
13
14 import akka.actor.testkit.typed.javadsl.ActorTestKit;
15 import akka.actor.typed.javadsl.Adapter;
16 import akka.cluster.Member;
17 import akka.cluster.MemberStatus;
18 import akka.cluster.typed.Cluster;
19 import akka.testkit.javadsl.TestKit;
20 import com.google.common.util.concurrent.Futures;
21 import com.google.common.util.concurrent.ListenableFuture;
22 import java.time.Duration;
23 import java.util.ArrayList;
24 import java.util.List;
25 import java.util.concurrent.TimeUnit;
26 import org.awaitility.Awaitility;
27 import org.junit.After;
28 import org.junit.Before;
29 import org.junit.Test;
30 import org.opendaylight.controller.eos.akka.AbstractNativeEosTest;
31 import org.opendaylight.mdsal.singleton.api.ClusterSingletonService;
32 import org.opendaylight.mdsal.singleton.api.ServiceGroupIdentifier;
33 import org.opendaylight.mdsal.singleton.impl.EOSClusterSingletonServiceProvider;
34 import org.opendaylight.yangtools.concepts.Registration;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
37
38 public class ClusterSingletonIntegrationTest extends AbstractNativeEosTest {
39
40     private static final Logger LOG = LoggerFactory.getLogger(ClusterSingletonIntegrationTest.class);
41
42     private AbstractNativeEosTest.MockNativeEntityOwnershipService node1;
43     private MockNativeEntityOwnershipService node2;
44     private MockNativeEntityOwnershipService node3;
45
46     private EOSClusterSingletonServiceProvider singletonNode1;
47     private EOSClusterSingletonServiceProvider singletonNode2;
48     private EOSClusterSingletonServiceProvider singletonNode3;
49
50
51     @Before
52     public void setUp() throws Exception {
53         node1 = startupNativeService(2550, List.of("member-1"), THREE_NODE_SEED_NODES);
54         node2 = startupNativeService(2551, List.of("member-2"), THREE_NODE_SEED_NODES);
55         node3 = startupNativeService(2552, List.of("member-3"), THREE_NODE_SEED_NODES);
56
57         singletonNode1 = new EOSClusterSingletonServiceProvider(node1);
58         singletonNode2 = new EOSClusterSingletonServiceProvider(node2);
59         singletonNode3 = new EOSClusterSingletonServiceProvider(node3);
60
61         waitUntillNodeReady(node3);
62     }
63
64     @After
65     public void tearDown() {
66         ActorTestKit.shutdown(Adapter.toTyped(node1.getActorSystem()), Duration.ofSeconds(20));
67         ActorTestKit.shutdown(Adapter.toTyped(node2.getActorSystem()), Duration.ofSeconds(20));
68         ActorTestKit.shutdown(Adapter.toTyped(node3.getActorSystem()), Duration.ofSeconds(20));
69     }
70
71     @Test
72     public void testSingletonOwnershipNotDropped() {
73         final MockClusterSingletonService service = new MockClusterSingletonService("member-1", "service-1");
74         singletonNode1.registerClusterSingletonService(service);
75
76         verifyServiceActive(service);
77
78         final MockClusterSingletonService service2 = new MockClusterSingletonService("member-2", "service-1");
79         singletonNode2.registerClusterSingletonService(service2);
80
81         verifyServiceInactive(service2, 2);
82     }
83
84     @Test
85     public void testSingletonOwnershipHandoff() {
86         final MockClusterSingletonService service = new MockClusterSingletonService("member-1", "service-1");
87         final Registration registration = singletonNode1.registerClusterSingletonService(service);
88
89         verifyServiceActive(service);
90
91         final MockClusterSingletonService service2 = new MockClusterSingletonService("member-2", "service-1");
92         singletonNode2.registerClusterSingletonService(service2);
93
94         verifyServiceInactive(service2, 2);
95
96         registration.close();
97         verifyServiceInactive(service);
98         verifyServiceActive(service2);
99     }
100
101     @Test
102     public void testSingletonOwnershipHandoffOnNodeShutdown() throws Exception {
103         MockClusterSingletonService service2 = new MockClusterSingletonService("member-2", "service-1");
104         Registration registration2 = singletonNode2.registerClusterSingletonService(service2);
105
106         verifyServiceActive(service2);
107
108         final MockClusterSingletonService service3 = new MockClusterSingletonService("member-3", "service-1");
109         final Registration registration3 = singletonNode3.registerClusterSingletonService(service3);
110
111         verifyServiceInactive(service3, 2);
112
113         LOG.debug("Shutting down node2");
114         TestKit.shutdownActorSystem(node2.getActorSystem());
115         verifyServiceActive(service3);
116
117         node2 = startupNativeService(2551, List.of("member-1"), THREE_NODE_SEED_NODES);
118         singletonNode2 = new EOSClusterSingletonServiceProvider(node2);
119
120         waitUntillNodeReady(node2);
121         service2 = new MockClusterSingletonService("member-2", "service-1");
122         singletonNode2.registerClusterSingletonService(service2);
123
124         verifyServiceActive(service3);
125         verifyServiceInactive(service2, 5);
126     }
127
128     private static void waitUntillNodeReady(final MockNativeEntityOwnershipService node) {
129         // need to wait until all nodes are ready
130         final Cluster cluster = Cluster.get(Adapter.toTyped(node.getActorSystem()));
131         Awaitility.await().atMost(Duration.ofSeconds(20)).until(() -> {
132             final List<Member> members = new ArrayList<>();
133             cluster.state().getMembers().forEach(members::add);
134             if (members.size() != 3) {
135                 return false;
136             }
137
138             for (final Member member : members) {
139                 if (!member.status().equals(MemberStatus.up())) {
140                     return false;
141                 }
142             }
143
144             return true;
145         });
146     }
147
148     private static void verifyServiceActive(final MockClusterSingletonService service) {
149         await().untilAsserted(() -> assertTrue(service.isActivated()));
150     }
151
152     private static void verifyServiceActive(final MockClusterSingletonService service, final long delay) {
153         await().pollDelay(delay, TimeUnit.SECONDS).untilAsserted(() -> assertTrue(service.isActivated()));
154     }
155
156     private static void verifyServiceInactive(final MockClusterSingletonService service) {
157         await().untilAsserted(() -> assertFalse(service.isActivated()));
158     }
159
160     private static void verifyServiceInactive(final MockClusterSingletonService service, final long delay) {
161         await().pollDelay(delay, TimeUnit.SECONDS).untilAsserted(() -> assertFalse(service.isActivated()));
162     }
163
164     private static class MockClusterSingletonService implements ClusterSingletonService {
165
166         private final String member;
167         private final ServiceGroupIdentifier identifier;
168         private boolean activated = false;
169
170         MockClusterSingletonService(final String member, final String identifier) {
171             this.member = member;
172             this.identifier = new ServiceGroupIdentifier(identifier);
173         }
174
175         @Override
176         public void instantiateServiceInstance() {
177             LOG.debug("{} : Activating service: {}", member, identifier);
178             activated = true;
179         }
180
181         @Override
182         public ListenableFuture<? extends Object> closeServiceInstance() {
183             LOG.debug("{} : Closing service: {}", member, identifier);
184             activated = false;
185             return Futures.immediateFuture(null);
186         }
187
188         @Override
189         public ServiceGroupIdentifier getIdentifier() {
190             return identifier;
191         }
192
193         public boolean isActivated() {
194             return activated;
195         }
196     }
197 }