Committed by
Yuta HIGUCHI
MastershipLoadBalancer: listen to RegionEvents
Simplify logic around scheduling a balancing task and listen to RegionEvents. Region Membership updates and Region Updates should also trigger a rebalance. Now, it is possible to queue up another balancing task even if there is one running. They'll still never run in parallel and will still run with at least a few seconds in between (30), but this way we don't ever risk missing an event that makes it necessary to rebalance - not even if we were rebalancing exactly when that event fired. Change-Id: I64e1c6fc5e87f2b1fffbefb54c96303dac55d1d1
Showing
1 changed file
with
59 additions
and
21 deletions
... | @@ -16,10 +16,6 @@ | ... | @@ -16,10 +16,6 @@ |
16 | 16 | ||
17 | package org.onosproject.mlb; | 17 | package org.onosproject.mlb; |
18 | 18 | ||
19 | -import com.google.common.util.concurrent.ListenableScheduledFuture; | ||
20 | -import com.google.common.util.concurrent.ListeningScheduledExecutorService; | ||
21 | -import com.google.common.util.concurrent.MoreExecutors; | ||
22 | - | ||
23 | import org.apache.felix.scr.annotations.Activate; | 19 | import org.apache.felix.scr.annotations.Activate; |
24 | import org.apache.felix.scr.annotations.Component; | 20 | import org.apache.felix.scr.annotations.Component; |
25 | import org.apache.felix.scr.annotations.Deactivate; | 21 | import org.apache.felix.scr.annotations.Deactivate; |
... | @@ -39,15 +35,19 @@ import org.onosproject.mastership.MastershipEvent; | ... | @@ -39,15 +35,19 @@ import org.onosproject.mastership.MastershipEvent; |
39 | import org.onosproject.mastership.MastershipListener; | 35 | import org.onosproject.mastership.MastershipListener; |
40 | import org.onosproject.mastership.MastershipService; | 36 | import org.onosproject.mastership.MastershipService; |
41 | import org.osgi.service.component.ComponentContext; | 37 | import org.osgi.service.component.ComponentContext; |
38 | +import org.onosproject.net.region.RegionEvent; | ||
39 | +import org.onosproject.net.region.RegionListener; | ||
40 | +import org.onosproject.net.region.RegionService; | ||
42 | import org.slf4j.Logger; | 41 | import org.slf4j.Logger; |
43 | 42 | ||
44 | import java.util.Dictionary; | 43 | import java.util.Dictionary; |
44 | +import java.util.concurrent.Executors; | ||
45 | import java.util.concurrent.Future; | 45 | import java.util.concurrent.Future; |
46 | +import java.util.concurrent.ScheduledExecutorService; | ||
46 | import java.util.concurrent.TimeUnit; | 47 | import java.util.concurrent.TimeUnit; |
47 | import java.util.concurrent.atomic.AtomicBoolean; | 48 | import java.util.concurrent.atomic.AtomicBoolean; |
48 | import java.util.concurrent.atomic.AtomicReference; | 49 | import java.util.concurrent.atomic.AtomicReference; |
49 | 50 | ||
50 | -import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor; | ||
51 | import static org.onlab.util.Tools.groupedThreads; | 51 | import static org.onlab.util.Tools.groupedThreads; |
52 | import static org.slf4j.LoggerFactory.getLogger; | 52 | import static org.slf4j.LoggerFactory.getLogger; |
53 | 53 | ||
... | @@ -62,7 +62,7 @@ public class MastershipLoadBalancer { | ... | @@ -62,7 +62,7 @@ public class MastershipLoadBalancer { |
62 | 62 | ||
63 | private final Logger log = getLogger(getClass()); | 63 | private final Logger log = getLogger(getClass()); |
64 | 64 | ||
65 | - private static final int DEFAULT_SCHEDULE_PERIOD = 5; | 65 | + private static final int DEFAULT_SCHEDULE_PERIOD = 30; |
66 | @Property(name = "schedulePeriod", intValue = DEFAULT_SCHEDULE_PERIOD, | 66 | @Property(name = "schedulePeriod", intValue = DEFAULT_SCHEDULE_PERIOD, |
67 | label = "Period to schedule balancing the mastership to be shared as evenly as by all online instances.") | 67 | label = "Period to schedule balancing the mastership to be shared as evenly as by all online instances.") |
68 | private int schedulePeriod = DEFAULT_SCHEDULE_PERIOD; | 68 | private int schedulePeriod = DEFAULT_SCHEDULE_PERIOD; |
... | @@ -85,6 +85,9 @@ public class MastershipLoadBalancer { | ... | @@ -85,6 +85,9 @@ public class MastershipLoadBalancer { |
85 | protected LeadershipService leadershipService; | 85 | protected LeadershipService leadershipService; |
86 | 86 | ||
87 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | 87 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) |
88 | + protected RegionService regionService; | ||
89 | + | ||
90 | + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | ||
88 | protected ClusterService clusterService; | 91 | protected ClusterService clusterService; |
89 | 92 | ||
90 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | 93 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) |
... | @@ -92,15 +95,24 @@ public class MastershipLoadBalancer { | ... | @@ -92,15 +95,24 @@ public class MastershipLoadBalancer { |
92 | 95 | ||
93 | private InnerLeadershipListener leadershipListener = new InnerLeadershipListener(); | 96 | private InnerLeadershipListener leadershipListener = new InnerLeadershipListener(); |
94 | 97 | ||
95 | - /* This listener is used to trigger balancing for any mastership event which will include switches changing state | 98 | + /* This listener is used to trigger balancing for any mastership event |
96 | - between active and inactive states as well as the same variety of event occurring with ONOS nodes. Must | 99 | + * which will include switches changing state between active and inactive |
97 | - use a listenable executor to ensure events are triggered with no frequency greater than once every 30 seconds. | 100 | + * states as well as the same variety of event occurring with ONOS nodes. |
98 | */ | 101 | */ |
99 | private InnerMastershipListener mastershipListener = new InnerMastershipListener(); | 102 | private InnerMastershipListener mastershipListener = new InnerMastershipListener(); |
100 | 103 | ||
101 | - //Ensures that all executions do not interfere with one another (single thread) | 104 | + /* Used to trigger balancing on region events where there was either a |
102 | - private ListeningScheduledExecutorService executorService = MoreExecutors. | 105 | + * change on the master sets of a given region or a change on the devices |
103 | - listeningDecorator(newSingleThreadScheduledExecutor(groupedThreads("MastershipLoadBalancer", "%d", log))); | 106 | + * that belong to a region. |
107 | + */ | ||
108 | + private InnerRegionListener regionEventListener = new InnerRegionListener(); | ||
109 | + | ||
110 | + /* Ensures that all executions do not interfere with one another (single | ||
111 | + * thread) and that they are apart from each other by at least what is | ||
112 | + * defined as the schedulePeriod. | ||
113 | + */ | ||
114 | + private ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor( | ||
115 | + groupedThreads("MastershipLoadBalancer", "%d", log)); | ||
104 | 116 | ||
105 | @Activate | 117 | @Activate |
106 | public void activate(ComponentContext context) { | 118 | public void activate(ComponentContext context) { |
... | @@ -110,6 +122,7 @@ public class MastershipLoadBalancer { | ... | @@ -110,6 +122,7 @@ public class MastershipLoadBalancer { |
110 | localId = clusterService.getLocalNode().id(); | 122 | localId = clusterService.getLocalNode().id(); |
111 | leadershipService.addListener(leadershipListener); | 123 | leadershipService.addListener(leadershipListener); |
112 | leadershipService.runForLeadership(REBALANCE_MASTERSHIP); | 124 | leadershipService.runForLeadership(REBALANCE_MASTERSHIP); |
125 | + regionService.addListener(regionEventListener); | ||
113 | log.info("Started"); | 126 | log.info("Started"); |
114 | } | 127 | } |
115 | 128 | ||
... | @@ -119,6 +132,7 @@ public class MastershipLoadBalancer { | ... | @@ -119,6 +132,7 @@ public class MastershipLoadBalancer { |
119 | mastershipService.removeListener(mastershipListener); | 132 | mastershipService.removeListener(mastershipListener); |
120 | leadershipService.withdraw(REBALANCE_MASTERSHIP); | 133 | leadershipService.withdraw(REBALANCE_MASTERSHIP); |
121 | leadershipService.removeListener(leadershipListener); | 134 | leadershipService.removeListener(leadershipListener); |
135 | + regionService.removeListener(regionEventListener); | ||
122 | cancelBalance(); | 136 | cancelBalance(); |
123 | executorService.shutdown(); | 137 | executorService.shutdown(); |
124 | log.info("Stopped"); | 138 | log.info("Stopped"); |
... | @@ -143,23 +157,34 @@ public class MastershipLoadBalancer { | ... | @@ -143,23 +157,34 @@ public class MastershipLoadBalancer { |
143 | } | 157 | } |
144 | } | 158 | } |
145 | 159 | ||
160 | + // Sets flag at execution to indicate there is currently a scheduled | ||
161 | + // rebalancing. As soon as it starts running, the flag is set back to | ||
162 | + // null and another rebalancing can be queued. | ||
146 | private void scheduleBalance() { | 163 | private void scheduleBalance() { |
147 | if (isLeader.get() && nextTask.get() == null) { | 164 | if (isLeader.get() && nextTask.get() == null) { |
148 | 165 | ||
149 | - ListenableScheduledFuture task = | 166 | + Future task = executorService.schedule(new BalanceTask(), |
150 | - executorService.schedule(mastershipAdminService::balanceRoles, | 167 | + schedulePeriod, TimeUnit.SECONDS); |
151 | - schedulePeriod, TimeUnit.SECONDS); | 168 | + |
152 | - task.addListener(() -> { | ||
153 | - log.info("Completed balance roles"); | ||
154 | - nextTask.set(null); | ||
155 | - }, MoreExecutors.directExecutor() | ||
156 | - ); | ||
157 | if (!nextTask.compareAndSet(null, task)) { | 169 | if (!nextTask.compareAndSet(null, task)) { |
158 | task.cancel(false); | 170 | task.cancel(false); |
159 | } | 171 | } |
160 | } | 172 | } |
161 | } | 173 | } |
162 | 174 | ||
175 | + private class BalanceTask implements Runnable { | ||
176 | + | ||
177 | + @Override | ||
178 | + public void run() { | ||
179 | + // nextTask is now running, free the spot so that it is possible | ||
180 | + // to queue up another upcoming task. | ||
181 | + nextTask.set(null); | ||
182 | + | ||
183 | + mastershipAdminService.balanceRoles(); | ||
184 | + log.info("Completed balance roles"); | ||
185 | + } | ||
186 | + } | ||
187 | + | ||
163 | private void cancelBalance() { | 188 | private void cancelBalance() { |
164 | Future task = nextTask.getAndSet(null); | 189 | Future task = nextTask.getAndSet(null); |
165 | if (task != null) { | 190 | if (task != null) { |
... | @@ -191,7 +216,6 @@ public class MastershipLoadBalancer { | ... | @@ -191,7 +216,6 @@ public class MastershipLoadBalancer { |
191 | 216 | ||
192 | @Override | 217 | @Override |
193 | public void event(MastershipEvent event) { | 218 | public void event(MastershipEvent event) { |
194 | - //Sets flag at execution to indicate there is currently a scheduled rebalancing, reverts upon completion | ||
195 | scheduleBalance(); | 219 | scheduleBalance(); |
196 | } | 220 | } |
197 | } | 221 | } |
... | @@ -207,4 +231,18 @@ public class MastershipLoadBalancer { | ... | @@ -207,4 +231,18 @@ public class MastershipLoadBalancer { |
207 | processLeaderChange(event.subject().leaderNodeId()); | 231 | processLeaderChange(event.subject().leaderNodeId()); |
208 | } | 232 | } |
209 | } | 233 | } |
234 | + | ||
235 | + private class InnerRegionListener implements RegionListener { | ||
236 | + @Override | ||
237 | + public void event(RegionEvent event) { | ||
238 | + switch (event.type()) { | ||
239 | + case REGION_MEMBERSHIP_CHANGED: | ||
240 | + case REGION_UPDATED: | ||
241 | + scheduleBalance(); | ||
242 | + break; | ||
243 | + default: | ||
244 | + break; | ||
245 | + } | ||
246 | + } | ||
247 | + } | ||
210 | } | 248 | } | ... | ... |
-
Please register or login to post a comment