Committed by
Gerrit Code Review
Fix for ONOS-3183
Stream processes by Lambda queries in collection objects has a known performance bottleneck. For each create and update link data operations, this kind of queries are frequently used and this causes inefficient topology discovery operation. For to solve that problem i analize thread dump during discovery process and i saw that especially getAllProviders method uses high cpu. than i change the provider search operation with additional map data which holds linkkey and its providers. by this way provider search operation not only gets faster but also uses minimum resource. Before that change, i can discover only 4XX number of switches at one controller, but later number grows to 15XX switches Change-Id: I65ed71b7f295917c818b2f9227d0fc070aa4a29b
Showing
1 changed file
with
53 additions
and
40 deletions
... | @@ -71,6 +71,7 @@ import org.slf4j.Logger; | ... | @@ -71,6 +71,7 @@ import org.slf4j.Logger; |
71 | 71 | ||
72 | import com.google.common.collect.Iterables; | 72 | import com.google.common.collect.Iterables; |
73 | import com.google.common.collect.Maps; | 73 | import com.google.common.collect.Maps; |
74 | +import com.google.common.collect.Sets; | ||
74 | import com.google.common.util.concurrent.Futures; | 75 | import com.google.common.util.concurrent.Futures; |
75 | 76 | ||
76 | import static com.google.common.base.Preconditions.checkNotNull; | 77 | import static com.google.common.base.Preconditions.checkNotNull; |
... | @@ -95,8 +96,8 @@ import static org.slf4j.LoggerFactory.getLogger; | ... | @@ -95,8 +96,8 @@ import static org.slf4j.LoggerFactory.getLogger; |
95 | @Component(immediate = true, enabled = true) | 96 | @Component(immediate = true, enabled = true) |
96 | @Service | 97 | @Service |
97 | public class ECLinkStore | 98 | public class ECLinkStore |
98 | - extends AbstractStore<LinkEvent, LinkStoreDelegate> | 99 | + extends AbstractStore<LinkEvent, LinkStoreDelegate> |
99 | - implements LinkStore { | 100 | + implements LinkStore { |
100 | 101 | ||
101 | /** | 102 | /** |
102 | * Modes for dealing with newly discovered links. | 103 | * Modes for dealing with newly discovered links. |
... | @@ -117,8 +118,10 @@ public class ECLinkStore | ... | @@ -117,8 +118,10 @@ public class ECLinkStore |
117 | private final Logger log = getLogger(getClass()); | 118 | private final Logger log = getLogger(getClass()); |
118 | 119 | ||
119 | private final Map<LinkKey, Link> links = Maps.newConcurrentMap(); | 120 | private final Map<LinkKey, Link> links = Maps.newConcurrentMap(); |
121 | + private final Map<LinkKey, Set<ProviderId>> linkProviders = Maps.newConcurrentMap(); | ||
120 | private EventuallyConsistentMap<Provided<LinkKey>, LinkDescription> linkDescriptions; | 122 | private EventuallyConsistentMap<Provided<LinkKey>, LinkDescription> linkDescriptions; |
121 | 123 | ||
124 | + | ||
122 | private ApplicationId appId; | 125 | private ApplicationId appId; |
123 | 126 | ||
124 | private static final MessageSubject LINK_INJECT_MESSAGE = new MessageSubject("inject-link-request"); | 127 | private static final MessageSubject LINK_INJECT_MESSAGE = new MessageSubject("inject-link-request"); |
... | @@ -188,10 +191,10 @@ public class ECLinkStore | ... | @@ -188,10 +191,10 @@ public class ECLinkStore |
188 | }).build(); | 191 | }).build(); |
189 | 192 | ||
190 | clusterCommunicator.addSubscriber(LINK_INJECT_MESSAGE, | 193 | clusterCommunicator.addSubscriber(LINK_INJECT_MESSAGE, |
191 | - SERIALIZER::decode, | 194 | + SERIALIZER::decode, |
192 | - this::injectLink, | 195 | + this::injectLink, |
193 | - SERIALIZER::encode, | 196 | + SERIALIZER::encode, |
194 | - SharedExecutors.getPoolThreadExecutor()); | 197 | + SharedExecutors.getPoolThreadExecutor()); |
195 | 198 | ||
196 | linkDescriptions.addListener(linkTracker); | 199 | linkDescriptions.addListener(linkTracker); |
197 | 200 | ||
... | @@ -202,6 +205,7 @@ public class ECLinkStore | ... | @@ -202,6 +205,7 @@ public class ECLinkStore |
202 | public void deactivate() { | 205 | public void deactivate() { |
203 | linkDescriptions.removeListener(linkTracker); | 206 | linkDescriptions.removeListener(linkTracker); |
204 | linkDescriptions.destroy(); | 207 | linkDescriptions.destroy(); |
208 | + linkProviders.clear(); | ||
205 | links.clear(); | 209 | links.clear(); |
206 | clusterCommunicator.removeSubscriber(LINK_INJECT_MESSAGE); | 210 | clusterCommunicator.removeSubscriber(LINK_INJECT_MESSAGE); |
207 | netCfgService.removeListener(cfgListener); | 211 | netCfgService.removeListener(cfgListener); |
... | @@ -273,10 +277,10 @@ public class ECLinkStore | ... | @@ -273,10 +277,10 @@ public class ECLinkStore |
273 | return null; | 277 | return null; |
274 | } | 278 | } |
275 | return Futures.getUnchecked(clusterCommunicator.sendAndReceive(new Provided<>(linkDescription, providerId), | 279 | return Futures.getUnchecked(clusterCommunicator.sendAndReceive(new Provided<>(linkDescription, providerId), |
276 | - LINK_INJECT_MESSAGE, | 280 | + LINK_INJECT_MESSAGE, |
277 | - SERIALIZER::encode, | 281 | + SERIALIZER::encode, |
278 | - SERIALIZER::decode, | 282 | + SERIALIZER::decode, |
279 | - dstNodeId)); | 283 | + dstNodeId)); |
280 | } | 284 | } |
281 | } | 285 | } |
282 | 286 | ||
... | @@ -295,16 +299,24 @@ public class ECLinkStore | ... | @@ -295,16 +299,24 @@ public class ECLinkStore |
295 | private LinkDescription createOrUpdateLinkInternal(LinkDescription current, LinkDescription updated) { | 299 | private LinkDescription createOrUpdateLinkInternal(LinkDescription current, LinkDescription updated) { |
296 | if (current != null) { | 300 | if (current != null) { |
297 | // we only allow transition from INDIRECT -> DIRECT | 301 | // we only allow transition from INDIRECT -> DIRECT |
298 | - return new DefaultLinkDescription( | 302 | + return new DefaultLinkDescription( |
299 | - current.src(), | 303 | + current.src(), |
300 | - current.dst(), | 304 | + current.dst(), |
301 | - current.type() == DIRECT ? DIRECT : updated.type(), | 305 | + current.type() == DIRECT ? DIRECT : updated.type(), |
302 | - current.isExpected(), | 306 | + current.isExpected(), |
303 | - union(current.annotations(), updated.annotations())); | 307 | + union(current.annotations(), updated.annotations())); |
304 | } | 308 | } |
305 | return updated; | 309 | return updated; |
306 | } | 310 | } |
307 | 311 | ||
312 | + private Set<ProviderId> createOrUpdateLinkProviders(Set<ProviderId> current, ProviderId providerId) { | ||
313 | + if (current == null) { | ||
314 | + current = Sets.newConcurrentHashSet(); | ||
315 | + } | ||
316 | + current.add(providerId); | ||
317 | + return current; | ||
318 | + } | ||
319 | + | ||
308 | private LinkEvent refreshLinkCache(LinkKey linkKey) { | 320 | private LinkEvent refreshLinkCache(LinkKey linkKey) { |
309 | AtomicReference<LinkEvent.Type> eventType = new AtomicReference<>(); | 321 | AtomicReference<LinkEvent.Type> eventType = new AtomicReference<>(); |
310 | Link link = links.compute(linkKey, (key, existingLink) -> { | 322 | Link link = links.compute(linkKey, (key, existingLink) -> { |
... | @@ -313,11 +325,11 @@ public class ECLinkStore | ... | @@ -313,11 +325,11 @@ public class ECLinkStore |
313 | eventType.set(LINK_ADDED); | 325 | eventType.set(LINK_ADDED); |
314 | return newLink; | 326 | return newLink; |
315 | } else if (existingLink.state() != newLink.state() || | 327 | } else if (existingLink.state() != newLink.state() || |
316 | - existingLink.isExpected() != newLink.isExpected() || | 328 | + existingLink.isExpected() != newLink.isExpected() || |
317 | - (existingLink.type() == INDIRECT && newLink.type() == DIRECT) || | 329 | + (existingLink.type() == INDIRECT && newLink.type() == DIRECT) || |
318 | - !AnnotationsUtil.isEqual(existingLink.annotations(), newLink.annotations())) { | 330 | + !AnnotationsUtil.isEqual(existingLink.annotations(), newLink.annotations())) { |
319 | - eventType.set(LINK_UPDATED); | 331 | + eventType.set(LINK_UPDATED); |
320 | - return newLink; | 332 | + return newLink; |
321 | } else { | 333 | } else { |
322 | return existingLink; | 334 | return existingLink; |
323 | } | 335 | } |
... | @@ -326,20 +338,16 @@ public class ECLinkStore | ... | @@ -326,20 +338,16 @@ public class ECLinkStore |
326 | } | 338 | } |
327 | 339 | ||
328 | private Set<ProviderId> getAllProviders(LinkKey linkKey) { | 340 | private Set<ProviderId> getAllProviders(LinkKey linkKey) { |
329 | - return linkDescriptions.keySet() | 341 | + return linkProviders.getOrDefault(linkKey, Sets.newConcurrentHashSet()); |
330 | - .stream() | ||
331 | - .filter(key -> key.key().equals(linkKey)) | ||
332 | - .map(key -> key.providerId()) | ||
333 | - .collect(Collectors.toSet()); | ||
334 | } | 342 | } |
335 | 343 | ||
336 | private ProviderId getBaseProviderId(LinkKey linkKey) { | 344 | private ProviderId getBaseProviderId(LinkKey linkKey) { |
337 | Set<ProviderId> allProviders = getAllProviders(linkKey); | 345 | Set<ProviderId> allProviders = getAllProviders(linkKey); |
338 | if (allProviders.size() > 0) { | 346 | if (allProviders.size() > 0) { |
339 | return allProviders.stream() | 347 | return allProviders.stream() |
340 | - .filter(p -> !p.isAncillary()) | 348 | + .filter(p -> !p.isAncillary()) |
341 | - .findFirst() | 349 | + .findFirst() |
342 | - .orElse(Iterables.getFirst(allProviders, null)); | 350 | + .orElse(Iterables.getFirst(allProviders, null)); |
343 | } | 351 | } |
344 | return null; | 352 | return null; |
345 | } | 353 | } |
... | @@ -356,11 +364,14 @@ public class ECLinkStore | ... | @@ -356,11 +364,14 @@ public class ECLinkStore |
356 | annotations.set(merge(annotations.get(), base.annotations())); | 364 | annotations.set(merge(annotations.get(), base.annotations())); |
357 | 365 | ||
358 | getAllProviders(linkKey).stream() | 366 | getAllProviders(linkKey).stream() |
359 | - .map(p -> new Provided<>(linkKey, p)) | 367 | + .map(p -> new Provided<>(linkKey, p)) |
360 | - .forEach(key -> { | 368 | + .forEach(key -> { |
361 | - annotations.set(merge(annotations.get(), | 369 | + LinkDescription linkDescription = linkDescriptions.get(key); |
362 | - linkDescriptions.get(key).annotations())); | 370 | + if (linkDescription != null) { |
363 | - }); | 371 | + annotations.set(merge(annotations.get(), |
372 | + linkDescription.annotations())); | ||
373 | + } | ||
374 | + }); | ||
364 | 375 | ||
365 | Link.State initialLinkState; | 376 | Link.State initialLinkState; |
366 | 377 | ||
... | @@ -375,8 +386,6 @@ public class ECLinkStore | ... | @@ -375,8 +386,6 @@ public class ECLinkStore |
375 | } | 386 | } |
376 | 387 | ||
377 | 388 | ||
378 | - | ||
379 | - | ||
380 | return DefaultLink.builder() | 389 | return DefaultLink.builder() |
381 | .providerId(baseProviderId) | 390 | .providerId(baseProviderId) |
382 | .src(src) | 391 | .src(src) |
... | @@ -394,8 +403,8 @@ public class ECLinkStore | ... | @@ -394,8 +403,8 @@ public class ECLinkStore |
394 | // Note: INDIRECT -> DIRECT transition only | 403 | // Note: INDIRECT -> DIRECT transition only |
395 | // so that BDDP discovered Link will not overwrite LDDP Link | 404 | // so that BDDP discovered Link will not overwrite LDDP Link |
396 | if (oldLink.state() != newLink.state() || | 405 | if (oldLink.state() != newLink.state() || |
397 | - (oldLink.type() == INDIRECT && newLink.type() == DIRECT) || | 406 | + (oldLink.type() == INDIRECT && newLink.type() == DIRECT) || |
398 | - !AnnotationsUtil.isEqual(oldLink.annotations(), newLink.annotations())) { | 407 | + !AnnotationsUtil.isEqual(oldLink.annotations(), newLink.annotations())) { |
399 | 408 | ||
400 | links.put(key, newLink); | 409 | links.put(key, newLink); |
401 | return new LinkEvent(LINK_UPDATED, newLink); | 410 | return new LinkEvent(LINK_UPDATED, newLink); |
... | @@ -447,6 +456,7 @@ public class ECLinkStore | ... | @@ -447,6 +456,7 @@ public class ECLinkStore |
447 | Link removedLink = links.remove(linkKey); | 456 | Link removedLink = links.remove(linkKey); |
448 | if (removedLink != null) { | 457 | if (removedLink != null) { |
449 | getAllProviders(linkKey).forEach(p -> linkDescriptions.remove(new Provided<>(linkKey, p))); | 458 | getAllProviders(linkKey).forEach(p -> linkDescriptions.remove(new Provided<>(linkKey, p))); |
459 | + linkProviders.remove(linkKey); | ||
450 | return new LinkEvent(LINK_REMOVED, removedLink); | 460 | return new LinkEvent(LINK_REMOVED, removedLink); |
451 | } | 461 | } |
452 | return null; | 462 | return null; |
... | @@ -475,9 +485,12 @@ public class ECLinkStore | ... | @@ -475,9 +485,12 @@ public class ECLinkStore |
475 | @Override | 485 | @Override |
476 | public void event(EventuallyConsistentMapEvent<Provided<LinkKey>, LinkDescription> event) { | 486 | public void event(EventuallyConsistentMapEvent<Provided<LinkKey>, LinkDescription> event) { |
477 | if (event.type() == PUT) { | 487 | if (event.type() == PUT) { |
488 | + linkProviders.compute(event.key().key(), (k, v) -> | ||
489 | + createOrUpdateLinkProviders(v, event.key().providerId())); | ||
478 | notifyDelegate(refreshLinkCache(event.key().key())); | 490 | notifyDelegate(refreshLinkCache(event.key().key())); |
479 | } else if (event.type() == REMOVE) { | 491 | } else if (event.type() == REMOVE) { |
480 | notifyDelegate(purgeLinkCache(event.key().key())); | 492 | notifyDelegate(purgeLinkCache(event.key().key())); |
493 | + linkProviders.remove(event.key().key()); | ||
481 | } | 494 | } |
482 | } | 495 | } |
483 | } | 496 | } |
... | @@ -521,8 +534,8 @@ public class ECLinkStore | ... | @@ -521,8 +534,8 @@ public class ECLinkStore |
521 | // Configuration properties factory | 534 | // Configuration properties factory |
522 | private final ConfigFactory factory = | 535 | private final ConfigFactory factory = |
523 | new ConfigFactory<ApplicationId, CoreConfig>(APP_SUBJECT_FACTORY, | 536 | new ConfigFactory<ApplicationId, CoreConfig>(APP_SUBJECT_FACTORY, |
524 | - CoreConfig.class, | 537 | + CoreConfig.class, |
525 | - "core") { | 538 | + "core") { |
526 | @Override | 539 | @Override |
527 | public CoreConfig createConfig() { | 540 | public CoreConfig createConfig() { |
528 | return new CoreConfig(); | 541 | return new CoreConfig(); | ... | ... |
-
Please register or login to post a comment