2 * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.controller.cluster.datastore;
10 import com.google.common.base.Preconditions;
11 import com.google.common.base.Verify;
12 import com.google.common.collect.Collections2;
13 import com.google.common.collect.Range;
14 import com.google.common.collect.RangeSet;
15 import com.google.common.collect.TreeRangeSet;
16 import com.google.common.primitives.UnsignedLong;
17 import java.util.HashMap;
19 import javax.annotation.Nonnull;
20 import javax.annotation.concurrent.NotThreadSafe;
21 import org.opendaylight.controller.cluster.access.concepts.ClientIdentifier;
22 import org.opendaylight.controller.cluster.access.concepts.LocalHistoryIdentifier;
23 import org.opendaylight.controller.cluster.access.concepts.TransactionIdentifier;
24 import org.opendaylight.controller.cluster.datastore.persisted.FrontendClientMetadata;
25 import org.opendaylight.controller.cluster.datastore.persisted.FrontendHistoryMetadata;
26 import org.opendaylight.yangtools.concepts.Builder;
27 import org.opendaylight.yangtools.concepts.Identifiable;
28 import org.slf4j.Logger;
29 import org.slf4j.LoggerFactory;
32 final class FrontendClientMetadataBuilder implements Builder<FrontendClientMetadata>, Identifiable<ClientIdentifier> {
33 private static final Logger LOG = LoggerFactory.getLogger(FrontendClientMetadataBuilder.class);
35 private final Map<LocalHistoryIdentifier, FrontendHistoryMetadataBuilder> currentHistories = new HashMap<>();
36 private final RangeSet<UnsignedLong> purgedHistories;
37 private final ClientIdentifier identifier;
39 FrontendClientMetadataBuilder(final ClientIdentifier identifier) {
40 this.identifier = Preconditions.checkNotNull(identifier);
41 purgedHistories = TreeRangeSet.create();
43 // History for stand-alone transactions is always present
44 final LocalHistoryIdentifier standaloneId = standaloneHistoryId();
45 currentHistories.put(standaloneId, new FrontendHistoryMetadataBuilder(standaloneId));
48 FrontendClientMetadataBuilder(final FrontendClientMetadata meta) {
49 this.identifier = Preconditions.checkNotNull(meta.getIdentifier());
50 purgedHistories = TreeRangeSet.create(meta.getPurgedHistories());
52 for (FrontendHistoryMetadata h : meta.getCurrentHistories()) {
53 final FrontendHistoryMetadataBuilder b = new FrontendHistoryMetadataBuilder(identifier, h);
54 currentHistories.put(b.getIdentifier(), b);
57 // Sanity check and recovery
58 final LocalHistoryIdentifier standaloneId = standaloneHistoryId();
59 if (!currentHistories.containsKey(standaloneId)) {
60 LOG.warn("Client {} recovered histories {} do not contain stand-alone history, attempting recovery",
61 identifier, currentHistories);
62 currentHistories.put(standaloneId, new FrontendHistoryMetadataBuilder(standaloneId));
66 private LocalHistoryIdentifier standaloneHistoryId() {
67 return new LocalHistoryIdentifier(identifier, 0);
71 public FrontendClientMetadata build() {
72 return new FrontendClientMetadata(identifier, purgedHistories,
73 Collections2.transform(currentHistories.values(), FrontendHistoryMetadataBuilder::build));
77 public ClientIdentifier getIdentifier() {
81 void onHistoryCreated(final LocalHistoryIdentifier historyId) {
82 final FrontendHistoryMetadataBuilder newMeta = new FrontendHistoryMetadataBuilder(historyId);
83 final FrontendHistoryMetadataBuilder oldMeta = currentHistories.putIfAbsent(historyId, newMeta);
84 if (oldMeta != null) {
85 // This should not be happening, warn about it
86 LOG.warn("Reused local history {}", historyId);
88 LOG.debug("Created local history {}", historyId);
92 void onHistoryClosed(final LocalHistoryIdentifier historyId) {
93 final FrontendHistoryMetadataBuilder builder = currentHistories.get(historyId);
94 if (builder != null) {
95 builder.onHistoryClosed();
96 LOG.debug("Closed history {}", historyId);
98 LOG.warn("Closed unknown history {}, ignoring", historyId);
102 void onHistoryPurged(final LocalHistoryIdentifier historyId) {
103 final FrontendHistoryMetadataBuilder history = currentHistories.remove(historyId);
104 if (history == null) {
105 LOG.warn("Purging unknown history {}", historyId);
108 // XXX: do we need to account for cookies?
109 purgedHistories.add(Range.singleton(UnsignedLong.fromLongBits(historyId.getHistoryId())));
110 LOG.debug("Purged history {}", historyId);
113 void onTransactionAborted(final TransactionIdentifier txId) {
114 final FrontendHistoryMetadataBuilder history = getHistory(txId);
115 if (history != null) {
116 history.onTransactionAborted(txId);
117 LOG.debug("Committed transaction {}", txId);
119 LOG.warn("Unknown history for aborted transaction {}, ignoring", txId);
123 void onTransactionCommitted(final TransactionIdentifier txId) {
124 final FrontendHistoryMetadataBuilder history = getHistory(txId);
125 if (history != null) {
126 history.onTransactionCommitted(txId);
127 LOG.debug("Aborted transaction {}", txId);
129 LOG.warn("Unknown history for commited transaction {}, ignoring", txId);
133 void onTransactionPurged(final TransactionIdentifier txId) {
134 final FrontendHistoryMetadataBuilder history = getHistory(txId);
135 if (history != null) {
136 history.onTransactionPurged(txId);
137 LOG.debug("Purged transaction {}", txId);
139 LOG.warn("Unknown history for purged transaction {}, ignoring", txId);
144 * Transform frontend metadata for a particular client into its {@link LeaderFrontendState} counterpart.
146 * @param shard parent shard
147 * @return Leader frontend state
149 @Nonnull LeaderFrontendState toLeaderState(@Nonnull final Shard shard) {
150 // Note: we have to make sure to *copy* all current state and not leak any views, otherwise leader/follower
151 // interactions would get intertwined leading to inconsistencies.
152 final Map<LocalHistoryIdentifier, LocalFrontendHistory> histories = new HashMap<>();
153 for (FrontendHistoryMetadataBuilder e : currentHistories.values()) {
154 if (e.getIdentifier().getHistoryId() != 0) {
155 final AbstractFrontendHistory state = e.toLeaderState(shard);
156 Verify.verify(state instanceof LocalFrontendHistory);
157 histories.put(e.getIdentifier(), (LocalFrontendHistory) state);
161 final AbstractFrontendHistory singleHistory;
162 final FrontendHistoryMetadataBuilder singleHistoryMeta = currentHistories.get(
163 new LocalHistoryIdentifier(identifier, 0));
164 if (singleHistoryMeta == null) {
165 final ShardDataTree tree = shard.getDataStore();
166 singleHistory = StandaloneFrontendHistory.create(shard.persistenceId(), getIdentifier(), tree);
168 singleHistory = singleHistoryMeta.toLeaderState(shard);
171 return new LeaderFrontendState(shard.persistenceId(), getIdentifier(), shard.getDataStore(),
172 TreeRangeSet.create(purgedHistories), singleHistory, histories);
175 private FrontendHistoryMetadataBuilder getHistory(final TransactionIdentifier txId) {
176 return currentHistories.get(txId.getHistoryId());