Fix followerDistributedDataStore tear down
[controller.git] / opendaylight / md-sal / sal-distributed-datastore / src / main / java / org / opendaylight / controller / cluster / datastore / FrontendMetadata.java
1 /*
2  * Copyright (c) 2016 Cisco Systems, Inc. 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.cluster.datastore;
9
10 import static com.google.common.base.Verify.verify;
11 import static java.util.Objects.requireNonNull;
12
13 import com.google.common.collect.Collections2;
14 import com.google.common.collect.ImmutableSet;
15 import com.google.common.collect.Maps;
16 import java.util.HashMap;
17 import java.util.Map;
18 import org.eclipse.jdt.annotation.NonNull;
19 import org.opendaylight.controller.cluster.access.concepts.ClientIdentifier;
20 import org.opendaylight.controller.cluster.access.concepts.FrontendIdentifier;
21 import org.opendaylight.controller.cluster.access.concepts.LocalHistoryIdentifier;
22 import org.opendaylight.controller.cluster.access.concepts.TransactionIdentifier;
23 import org.opendaylight.controller.cluster.datastore.persisted.FrontendClientMetadata;
24 import org.opendaylight.controller.cluster.datastore.persisted.FrontendShardDataTreeSnapshotMetadata;
25 import org.slf4j.Logger;
26 import org.slf4j.LoggerFactory;
27
28 /**
29  * Frontend state as observed by a shard follower. This class is responsible for maintaining metadata state
30  * so that this can be used to seed {@link LeaderFrontendState} with proper state so that the frontend/backend
31  * conversation can continue where it left off. This class is NOT thread-safe.
32  *
33  * @author Robert Varga
34  */
35 final class FrontendMetadata extends ShardDataTreeMetadata<FrontendShardDataTreeSnapshotMetadata> {
36     private static final Logger LOG = LoggerFactory.getLogger(FrontendMetadata.class);
37
38     private final Map<FrontendIdentifier, FrontendClientMetadataBuilder> clients = new HashMap<>();
39     private final String shardName;
40
41     FrontendMetadata(final String shardName) {
42         this.shardName = requireNonNull(shardName);
43     }
44
45     @Override
46     Class<FrontendShardDataTreeSnapshotMetadata> getSupportedType() {
47         return FrontendShardDataTreeSnapshotMetadata.class;
48     }
49
50     @Override
51     void reset() {
52         LOG.debug("{}: clearing clients {}", shardName, clients);
53         clients.clear();
54     }
55
56     @Override
57     void doApplySnapshot(final FrontendShardDataTreeSnapshotMetadata snapshot) {
58         LOG.debug("{}: applying snapshot {} over clients {}", shardName, snapshot, clients);
59         clients.clear();
60
61         for (FrontendClientMetadata m : snapshot.getClients()) {
62             LOG.debug("{}: applying metadata {}", shardName, m);
63             final FrontendClientMetadataBuilder b = FrontendClientMetadataBuilder.of(shardName, m);
64             final FrontendIdentifier client = m.getIdentifier().getFrontendId();
65
66             LOG.debug("{}: client {} updated to {}", shardName, client, b);
67             clients.put(client, b);
68         }
69     }
70
71     @Override
72     FrontendShardDataTreeSnapshotMetadata toSnapshot() {
73         return new FrontendShardDataTreeSnapshotMetadata(Collections2.transform(clients.values(),
74             FrontendClientMetadataBuilder::build));
75     }
76
77     private FrontendClientMetadataBuilder ensureClient(final ClientIdentifier id) {
78         final FrontendClientMetadataBuilder existing = clients.get(id.getFrontendId());
79         if (existing != null && id.equals(existing.getIdentifier())) {
80             return existing;
81         }
82
83         final FrontendClientMetadataBuilder client = new FrontendClientMetadataBuilder.Enabled(shardName, id);
84         final FrontendClientMetadataBuilder previous = clients.put(id.getFrontendId(), client);
85         if (previous != null) {
86             LOG.debug("{}: Replaced client {} with {}", shardName, previous, client);
87         } else {
88             LOG.debug("{}: Added client {}", shardName, client);
89         }
90         return client;
91     }
92
93     @Override
94     void onHistoryCreated(final LocalHistoryIdentifier historyId) {
95         ensureClient(historyId.getClientId()).onHistoryCreated(historyId);
96     }
97
98     @Override
99     void onHistoryClosed(final LocalHistoryIdentifier historyId) {
100         ensureClient(historyId.getClientId()).onHistoryClosed(historyId);
101     }
102
103     @Override
104     void onHistoryPurged(final LocalHistoryIdentifier historyId) {
105         ensureClient(historyId.getClientId()).onHistoryPurged(historyId);
106     }
107
108     @Override
109     void onTransactionAborted(final TransactionIdentifier txId) {
110         ensureClient(txId.getHistoryId().getClientId()).onTransactionAborted(txId);
111     }
112
113     @Override
114     void onTransactionCommitted(final TransactionIdentifier txId) {
115         ensureClient(txId.getHistoryId().getClientId()).onTransactionCommitted(txId);
116     }
117
118     @Override
119     void onTransactionPurged(final TransactionIdentifier txId) {
120         ensureClient(txId.getHistoryId().getClientId()).onTransactionPurged(txId);
121     }
122
123     /**
124      * Transform frontend metadata into an active leader state map.
125      *
126      * @return Leader frontend state
127      */
128     @NonNull Map<FrontendIdentifier, LeaderFrontendState> toLeaderState(final @NonNull Shard shard) {
129         return new HashMap<>(Maps.transformValues(clients, meta -> meta.toLeaderState(shard)));
130     }
131
132     void disableTracking(final ClientIdentifier clientId) {
133         final FrontendIdentifier frontendId = clientId.getFrontendId();
134         final FrontendClientMetadataBuilder client = clients.get(frontendId);
135         if (client == null) {
136             // When we havent seen the client before, we still need to disable tracking for him since this only gets
137             // triggered once.
138             LOG.debug("{}: disableTracking {} does not match any client, pre-disabling client.", shardName, clientId);
139             clients.put(frontendId, new FrontendClientMetadataBuilder.Disabled(shardName, clientId));
140             return;
141         }
142         if (!clientId.equals(client.getIdentifier())) {
143             LOG.debug("{}: disableTracking {} does not match client {}, ignoring", shardName, clientId, client);
144             return;
145         }
146         if (client instanceof FrontendClientMetadataBuilder.Disabled) {
147             LOG.debug("{}: client {} is has already disabled tracking", shardName, client);
148             return;
149         }
150
151         verify(clients.replace(frontendId, client, new FrontendClientMetadataBuilder.Disabled(shardName, clientId)));
152     }
153
154     ImmutableSet<ClientIdentifier> getClients() {
155         return clients.values().stream()
156                 .map(FrontendClientMetadataBuilder::getIdentifier)
157                 .collect(ImmutableSet.toImmutableSet());
158     }
159 }