BUG-7976: Race between peer removal and routes update
[bgpcep.git] / bgp / rib-impl / src / main / java / org / opendaylight / protocol / bgp / rib / impl / ExportPolicyPeerTrackerImpl.java
index 4d5bbdbbb8d73e56f72d5c1d80c4a5437d515d50..be0f6a4c32134e6b861b8aa154b18ca491a9cef3 100644 (file)
@@ -7,23 +7,17 @@
  */
 package org.opendaylight.protocol.bgp.rib.impl;
 
-import static java.util.function.Function.identity;
-import static java.util.stream.Collectors.toMap;
-
 import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableMap;
-import java.util.Collections;
 import java.util.EnumMap;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
-import java.util.stream.Collectors;
 import javax.annotation.concurrent.GuardedBy;
 import javax.annotation.concurrent.ThreadSafe;
+import org.opendaylight.protocol.bgp.rib.impl.spi.PeerExportGroupRegistry;
 import org.opendaylight.protocol.bgp.rib.spi.ExportPolicyPeerTracker;
-import org.opendaylight.protocol.bgp.rib.spi.IdentifierUtils;
 import org.opendaylight.protocol.bgp.rib.spi.PeerExportGroup;
 import org.opendaylight.protocol.bgp.rib.spi.PeerExportGroup.PeerExporTuple;
 import org.opendaylight.protocol.concepts.AbstractRegistration;
@@ -47,28 +41,36 @@ final class ExportPolicyPeerTrackerImpl implements ExportPolicyPeerTracker {
     private final Set<PeerId> peerTables = new HashSet<>();
     private final PolicyDatabase policyDatabase;
     private final TablesKey localTableKey;
-    private volatile Map<PeerRole, PeerExportGroup> groups = Collections.emptyMap();
+    @GuardedBy("this")
+    private final Map<PeerRole, PeerExportGroupRegistry> groups = new EnumMap<>(PeerRole.class);
 
     ExportPolicyPeerTrackerImpl(final PolicyDatabase policyDatabase, final TablesKey localTablesKey) {
         this.policyDatabase = Preconditions.checkNotNull(policyDatabase);
         this.localTableKey = localTablesKey;
     }
 
-    private synchronized void createGroups(final Map<YangInstanceIdentifier, PeerRole> peerPathRoles) {
-        if (!peerPathRoles.isEmpty()) {
-            final Map<PeerRole, Map<PeerId, PeerExporTuple>> immutablePeers = peerPathRoles.entrySet().stream()
-                .collect(Collectors.groupingBy(Map.Entry::getValue, toMap(peer -> IdentifierUtils.peerKeyToPeerId(peer
-                    .getKey()), peer -> new PeerExporTuple(peer.getKey(), peer.getValue()))));
+    private synchronized AbstractRegistration addToExportGroups(final PeerId peerId,
+        final YangInstanceIdentifier peerPath, final PeerRole peerRole) {
+        final PeerExportGroupRegistry peerExp = this.groups.computeIfAbsent(peerRole,
+            k -> new PeerExportGroupImpl(this.policyDatabase.exportPolicyForRole(peerRole)));
 
-            this.groups = peerPathRoles.values().stream().collect(Collectors.toSet()).stream()
-                .collect(toMap(identity(), role -> new PeerExportGroupImpl(ImmutableMap.copyOf(immutablePeers.get(role)),
-                    this.policyDatabase.exportPolicyForRole(role)), (oldKey, newKey) -> oldKey, () -> new EnumMap<>(PeerRole.class)));
-        }
+        final AbstractRegistration registration =  peerExp.registerPeer(peerId, new PeerExporTuple(peerPath, peerRole));
+
+        return new AbstractRegistration() {
+            @Override
+            protected void removeRegistration() {
+                registration.close();
+                if(ExportPolicyPeerTrackerImpl.this.groups.get(peerRole).isEmpty()) {
+                    ExportPolicyPeerTrackerImpl.this.groups.remove(peerRole);
+                }
+            }
+        };
     }
 
     @Override
-    public synchronized AbstractRegistration registerPeer(final PeerId peerId, final SendReceive sendReceive, final YangInstanceIdentifier peerPath,
-        final PeerRole peerRole, final Optional<SimpleRoutingPolicy> optSimpleRoutingPolicy) {
+    public synchronized AbstractRegistration registerPeer(final PeerId peerId, final SendReceive sendReceive,
+        final YangInstanceIdentifier peerPath, final PeerRole peerRole,
+        final Optional<SimpleRoutingPolicy> optSimpleRoutingPolicy) {
         if (sendReceive != null) {
             this.peerAddPathTables.put(peerId, sendReceive);
             LOG.debug("Supported Add BestPath table {} added to peer {}", sendReceive, peerId);
@@ -79,21 +81,21 @@ final class ExportPolicyPeerTrackerImpl implements ExportPolicyPeerTracker {
         }
         this.peerRoles.put(peerPath, peerRole);
         LOG.debug("Supported table {} added to peer {} role {}", this.localTableKey, peerId, peerRole);
-        createGroups(this.peerRoles);
+        final AbstractRegistration registration = addToExportGroups(peerId, peerPath, peerRole);
 
         final Object lock = this;
         return new AbstractRegistration() {
             @Override
             protected void removeRegistration() {
                 synchronized (lock) {
-                    final SendReceive sendReceiveValue = peerAddPathTables.remove(peerId);
+                    final SendReceive sendReceiveValue = ExportPolicyPeerTrackerImpl.this.peerAddPathTables.remove(peerId);
                     if (sendReceiveValue != null) {
                         LOG.debug("Supported Add BestPath table {} removed to peer {}", sendReceiveValue, peerId);
                     }
-                    peerTables.remove(peerId);
-                    LOG.debug("Removed peer {} from supported table {}", peerId, localTableKey);
-                    peerRoles.remove(peerPath);
-                    createGroups(peerRoles);
+                    ExportPolicyPeerTrackerImpl.this.peerTables.remove(peerId);
+                    LOG.debug("Removed peer {} from supported table {}", peerId, ExportPolicyPeerTrackerImpl.this.localTableKey);
+                    ExportPolicyPeerTrackerImpl.this.peerRoles.remove(peerPath);
+                    registration.close();
                 }
             }
         };
@@ -119,5 +121,4 @@ final class ExportPolicyPeerTrackerImpl implements ExportPolicyPeerTracker {
         final SendReceive sendReceive = this.peerAddPathTables.get(peerId);
         return sendReceive != null && (sendReceive.equals(SendReceive.Both) || sendReceive.equals(SendReceive.Receive));
     }
-
 }
\ No newline at end of file