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.FrontendShardDataTreeSnapshotMetadata;
24 import org.opendaylight.controller.cluster.datastore.utils.ImmutableUnsignedLongSet;
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 (var clientMeta : snapshot.getClients()) {
62             LOG.debug("{}: applying metadata {}", shardName, clientMeta);
63             final var builder = FrontendClientMetadataBuilder.of(shardName, clientMeta);
64             final var frontendId = clientMeta.clientId().getFrontendId();
65
66             LOG.debug("{}: client {} updated to {}", shardName, frontendId, builder);
67             clients.put(frontendId, builder);
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 var existing = clients.get(id.getFrontendId());
79         if (existing != null && id.equals(existing.clientId())) {
80             return existing;
81         }
82
83         final var client = new FrontendClientMetadataBuilder.Enabled(shardName, id);
84         final var 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     @Override
124     void onTransactionsSkipped(final LocalHistoryIdentifier historyId, final ImmutableUnsignedLongSet txIds) {
125         ensureClient(historyId.getClientId()).onTransactionsSkipped(historyId, txIds);
126     }
127
128     /**
129      * Transform frontend metadata into an active leader state map.
130      *
131      * @return Leader frontend state
132      */
133     @NonNull Map<FrontendIdentifier, LeaderFrontendState> toLeaderState(final @NonNull Shard shard) {
134         return new HashMap<>(Maps.transformValues(clients, meta -> meta.toLeaderState(shard)));
135     }
136
137     void disableTracking(final ClientIdentifier clientId) {
138         final var frontendId = clientId.getFrontendId();
139         final var client = clients.get(frontendId);
140         if (client == null) {
141             // When we have not seen the client before, we still need to disable tracking for him since this only gets
142             // triggered once.
143             LOG.debug("{}: disableTracking {} does not match any client, pre-disabling client.", shardName, clientId);
144             clients.put(frontendId, new FrontendClientMetadataBuilder.Disabled(shardName, clientId));
145             return;
146         }
147         if (!clientId.equals(client.clientId())) {
148             LOG.debug("{}: disableTracking {} does not match client {}, ignoring", shardName, clientId, client);
149             return;
150         }
151         if (client instanceof FrontendClientMetadataBuilder.Disabled) {
152             LOG.debug("{}: client {} is has already disabled tracking", shardName, client);
153             return;
154         }
155
156         verify(clients.replace(frontendId, client, new FrontendClientMetadataBuilder.Disabled(shardName, clientId)));
157     }
158
159     ImmutableSet<ClientIdentifier> getClients() {
160         return clients.values().stream()
161             .map(FrontendClientMetadataBuilder::clientId)
162             .collect(ImmutableSet.toImmutableSet());
163     }
164 }