e11778da87183bcd7033d28d81e8d175054ca228
[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
12 import com.google.common.base.Preconditions;
13 import com.google.common.collect.Collections2;
14 import com.google.common.collect.Maps;
15 import java.util.HashMap;
16 import java.util.Map;
17 import javax.annotation.Nonnull;
18 import javax.annotation.concurrent.NotThreadSafe;
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.
32  *
33  * @author Robert Varga
34  */
35 @NotThreadSafe
36 final class FrontendMetadata extends ShardDataTreeMetadata<FrontendShardDataTreeSnapshotMetadata> {
37     private static final Logger LOG = LoggerFactory.getLogger(FrontendMetadata.class);
38
39     private final Map<FrontendIdentifier, FrontendClientMetadataBuilder> clients = new HashMap<>();
40     private final String shardName;
41
42     FrontendMetadata(final String shardName) {
43         this.shardName = Preconditions.checkNotNull(shardName);
44     }
45
46     @Override
47     Class<FrontendShardDataTreeSnapshotMetadata> getSupportedType() {
48         return FrontendShardDataTreeSnapshotMetadata.class;
49     }
50
51     @Override
52     void reset() {
53         LOG.debug("{}: clearing clients {}", shardName, clients);
54         clients.clear();
55     }
56
57     @Override
58     void doApplySnapshot(final FrontendShardDataTreeSnapshotMetadata snapshot) {
59         LOG.debug("{}: applying snapshot {} over clients {}", shardName, snapshot, clients);
60         clients.clear();
61
62         for (FrontendClientMetadata m : snapshot.getClients()) {
63             LOG.debug("{}: applying metadata {}", shardName, m);
64             final FrontendClientMetadataBuilder b = FrontendClientMetadataBuilder.of(shardName, m);
65             final FrontendIdentifier client = m.getIdentifier().getFrontendId();
66
67             LOG.debug("{}: client {} updated to {}", shardName, client, b);
68             clients.put(client, b);
69         }
70     }
71
72     @Override
73     FrontendShardDataTreeSnapshotMetadata toSnapshot() {
74         return new FrontendShardDataTreeSnapshotMetadata(Collections2.transform(clients.values(),
75             FrontendClientMetadataBuilder::build));
76     }
77
78     private FrontendClientMetadataBuilder ensureClient(final ClientIdentifier id) {
79         final FrontendClientMetadataBuilder existing = clients.get(id.getFrontendId());
80         if (existing != null && id.equals(existing.getIdentifier())) {
81             return existing;
82         }
83
84         final FrontendClientMetadataBuilder client = new FrontendClientMetadataBuilder.Enabled(shardName, id);
85         final FrontendClientMetadataBuilder previous = clients.put(id.getFrontendId(), client);
86         if (previous != null) {
87             LOG.debug("{}: Replaced client {} with {}", shardName, previous, client);
88         } else {
89             LOG.debug("{}: Added client {}", shardName, client);
90         }
91         return client;
92     }
93
94     @Override
95     void onHistoryCreated(final LocalHistoryIdentifier historyId) {
96         ensureClient(historyId.getClientId()).onHistoryCreated(historyId);
97     }
98
99     @Override
100     void onHistoryClosed(final LocalHistoryIdentifier historyId) {
101         ensureClient(historyId.getClientId()).onHistoryClosed(historyId);
102     }
103
104     @Override
105     void onHistoryPurged(final LocalHistoryIdentifier historyId) {
106         ensureClient(historyId.getClientId()).onHistoryPurged(historyId);
107     }
108
109     @Override
110     void onTransactionAborted(final TransactionIdentifier txId) {
111         ensureClient(txId.getHistoryId().getClientId()).onTransactionAborted(txId);
112     }
113
114     @Override
115     void onTransactionCommitted(final TransactionIdentifier txId) {
116         ensureClient(txId.getHistoryId().getClientId()).onTransactionCommitted(txId);
117     }
118
119     @Override
120     void onTransactionPurged(final TransactionIdentifier txId) {
121         ensureClient(txId.getHistoryId().getClientId()).onTransactionPurged(txId);
122     }
123
124     /**
125      * Transform frontend metadata into an active leader state map.
126      *
127      * @return Leader frontend state
128      */
129     @Nonnull Map<FrontendIdentifier, LeaderFrontendState> toLeaderState(@Nonnull final Shard shard) {
130         return new HashMap<>(Maps.transformValues(clients, meta -> meta.toLeaderState(shard)));
131     }
132
133     void disableTracking(final ClientIdentifier clientId) {
134         final FrontendIdentifier frontendId = clientId.getFrontendId();
135         final FrontendClientMetadataBuilder client = clients.get(frontendId);
136         if (client == null) {
137             LOG.debug("{}: disableTracking {} does not match any client, ignoring", shardName, clientId);
138             return;
139         }
140         if (!clientId.equals(client.getIdentifier())) {
141             LOG.debug("{}: disableTracking {} does not match client {}, ignoring", shardName, clientId, client);
142             return;
143         }
144         if (client instanceof FrontendClientMetadataBuilder.Disabled) {
145             LOG.debug("{}: client {} is has already disabled tracking", shardName, client);
146             return;
147         }
148
149         verify(clients.replace(frontendId, client, new FrontendClientMetadataBuilder.Disabled(shardName, clientId)));
150     }
151 }