BUG-5280: add a transction purge step
[controller.git] / opendaylight / md-sal / sal-distributed-datastore / src / main / java / org / opendaylight / controller / cluster / datastore / AbstractFrontendHistory.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 com.google.common.base.MoreObjects;
11 import com.google.common.base.Preconditions;
12 import com.google.common.base.Ticker;
13 import java.util.HashMap;
14 import java.util.Map;
15 import java.util.Optional;
16 import javax.annotation.Nullable;
17 import org.opendaylight.controller.cluster.access.commands.AbstractReadTransactionRequest;
18 import org.opendaylight.controller.cluster.access.commands.CommitLocalTransactionRequest;
19 import org.opendaylight.controller.cluster.access.commands.OutOfOrderRequestException;
20 import org.opendaylight.controller.cluster.access.commands.TransactionPurgeRequest;
21 import org.opendaylight.controller.cluster.access.commands.TransactionPurgeResponse;
22 import org.opendaylight.controller.cluster.access.commands.TransactionRequest;
23 import org.opendaylight.controller.cluster.access.commands.TransactionSuccess;
24 import org.opendaylight.controller.cluster.access.concepts.LocalHistoryIdentifier;
25 import org.opendaylight.controller.cluster.access.concepts.RequestEnvelope;
26 import org.opendaylight.controller.cluster.access.concepts.RequestException;
27 import org.opendaylight.controller.cluster.access.concepts.TransactionIdentifier;
28 import org.opendaylight.yangtools.concepts.Identifiable;
29 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory;
32
33 /**
34  * Abstract class for providing logical tracking of frontend local histories. This class is specialized for
35  * standalone transactions and chained transactions.
36  *
37  * @author Robert Varga
38  */
39 abstract class AbstractFrontendHistory implements Identifiable<LocalHistoryIdentifier> {
40     private static final Logger LOG = LoggerFactory.getLogger(AbstractFrontendHistory.class);
41     private static final OutOfOrderRequestException UNSEQUENCED_START = new OutOfOrderRequestException(0);
42
43     private final Map<TransactionIdentifier, FrontendTransaction> transactions = new HashMap<>();
44     private final String persistenceId;
45     private final Ticker ticker;
46
47     AbstractFrontendHistory(final String persistenceId, final Ticker ticker) {
48         this.persistenceId = Preconditions.checkNotNull(persistenceId);
49         this.ticker = Preconditions.checkNotNull(ticker);
50     }
51
52     final String persistenceId() {
53         return persistenceId;
54     }
55
56     final long readTime() {
57         return ticker.read();
58     }
59
60     final @Nullable TransactionSuccess<?> handleTransactionRequest(final TransactionRequest<?> request,
61             final RequestEnvelope envelope, final long now) throws RequestException {
62         final TransactionIdentifier id = request.getTarget();
63
64         FrontendTransaction tx;
65         if (request instanceof TransactionPurgeRequest) {
66             tx = transactions.remove(id);
67             if (tx == null) {
68                 // We have no record of the transaction, nothing to do
69                 LOG.debug("{}: no state for transaction {}, purge is complete", persistenceId(), id);
70                 return new TransactionPurgeResponse(id, request.getSequence());
71             }
72         } else {
73             tx = transactions.get(id);
74             if (tx == null) {
75                 // The transaction does not exist and we are about to create it, check sequence number
76                 if (request.getSequence() != 0) {
77                     LOG.debug("{}: no transaction state present, unexpected request {}", persistenceId(), request);
78                     throw UNSEQUENCED_START;
79                 }
80
81                 tx = createTransaction(request, id);
82                 transactions.put(id, tx);
83             } else {
84                 final Optional<TransactionSuccess<?>> maybeReplay = tx.replaySequence(request.getSequence());
85                 if (maybeReplay.isPresent()) {
86                     final TransactionSuccess<?> replay = maybeReplay.get();
87                     LOG.debug("{}: envelope {} replaying response {}", persistenceId(), envelope, replay);
88                     return replay;
89                 }
90             }
91         }
92
93         return tx.handleRequest(request, envelope, now);
94     }
95
96     private FrontendTransaction createTransaction(final TransactionRequest<?> request, final TransactionIdentifier id)
97             throws RequestException {
98         if (request instanceof CommitLocalTransactionRequest) {
99             LOG.debug("{}: allocating new ready transaction {}", persistenceId(), id);
100             return createReadyTransaction(id, ((CommitLocalTransactionRequest) request).getModification());
101         }
102         if (request instanceof AbstractReadTransactionRequest) {
103             if (((AbstractReadTransactionRequest<?>) request).isSnapshotOnly()) {
104                 LOG.debug("{}: allocatint new open snapshot {}", persistenceId(), id);
105                 return createOpenSnapshot(id);
106             }
107         }
108
109         LOG.debug("{}: allocating new open transaction {}", persistenceId(), id);
110         return createOpenTransaction(id);
111     }
112
113     abstract FrontendTransaction createOpenSnapshot(TransactionIdentifier id) throws RequestException;
114
115     abstract FrontendTransaction createOpenTransaction(TransactionIdentifier id) throws RequestException;
116
117     abstract FrontendTransaction createReadyTransaction(TransactionIdentifier id, DataTreeModification mod)
118         throws RequestException;
119
120     abstract ShardDataTreeCohort createReadyCohort(TransactionIdentifier id, DataTreeModification mod);
121
122     @Override
123     public String toString() {
124         return MoreObjects.toStringHelper(this).omitNullValues().add("identifier", getIdentifier())
125                 .add("persistenceId", persistenceId).add("transactions", transactions).toString();
126     }
127 }