X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;ds=inline;f=opendaylight%2Fmd-sal%2Fsal-distributed-datastore%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fcontroller%2Fcluster%2Fdatastore%2FShard.java;h=9cb015cfaf35796bdf98692d8d98b9b67dd86a13;hb=f276ae33b951d173b51c467bb7bb1a5f5cf9a1e6;hp=dabad41aff97b81ba83dcdf1626dca26e987a1b1;hpb=9402730d96f9633d2165de99fad65768d8537a9a;p=controller.git
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/Shard.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/Shard.java
index dabad41aff..9cb015cfaf 100644
--- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/Shard.java
+++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/Shard.java
@@ -12,18 +12,20 @@ import akka.actor.ActorRef;
import akka.actor.ActorSelection;
import akka.actor.Cancellable;
import akka.actor.Props;
-import akka.japi.Creator;
-import akka.persistence.RecoveryFailure;
import akka.serialization.Serialization;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import java.io.IOException;
-import java.util.HashMap;
+import java.util.Collection;
+import java.util.Collections;
import java.util.Map;
+import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nonnull;
import org.opendaylight.controller.cluster.common.actor.CommonConfig;
+import org.opendaylight.controller.cluster.common.actor.MessageTracker;
+import org.opendaylight.controller.cluster.common.actor.MessageTracker.Error;
import org.opendaylight.controller.cluster.common.actor.MeteringBehavior;
import org.opendaylight.controller.cluster.datastore.ShardCommitCoordinator.CohortEntry;
import org.opendaylight.controller.cluster.datastore.exceptions.NoShardLeaderException;
@@ -40,6 +42,8 @@ import org.opendaylight.controller.cluster.datastore.messages.CommitTransaction;
import org.opendaylight.controller.cluster.datastore.messages.CommitTransactionReply;
import org.opendaylight.controller.cluster.datastore.messages.CreateTransaction;
import org.opendaylight.controller.cluster.datastore.messages.CreateTransactionReply;
+import org.opendaylight.controller.cluster.datastore.messages.DatastoreSnapshot;
+import org.opendaylight.controller.cluster.datastore.messages.DatastoreSnapshot.ShardSnapshot;
import org.opendaylight.controller.cluster.datastore.messages.ForwardedReadyTransaction;
import org.opendaylight.controller.cluster.datastore.messages.GetShardDataTree;
import org.opendaylight.controller.cluster.datastore.messages.PeerAddressResolved;
@@ -49,10 +53,8 @@ import org.opendaylight.controller.cluster.datastore.messages.RegisterDataTreeCh
import org.opendaylight.controller.cluster.datastore.messages.ShardLeaderStateChanged;
import org.opendaylight.controller.cluster.datastore.messages.UpdateSchemaContext;
import org.opendaylight.controller.cluster.datastore.modification.Modification;
-import org.opendaylight.controller.cluster.datastore.modification.ModificationPayload;
import org.opendaylight.controller.cluster.datastore.modification.MutableCompositeModification;
import org.opendaylight.controller.cluster.datastore.utils.Dispatchers;
-import org.opendaylight.controller.cluster.datastore.utils.MessageTracker;
import org.opendaylight.controller.cluster.notifications.LeaderStateChanged;
import org.opendaylight.controller.cluster.notifications.RegisterRoleChangeListener;
import org.opendaylight.controller.cluster.notifications.RoleChangeNotifier;
@@ -62,12 +64,13 @@ import org.opendaylight.controller.cluster.raft.RaftActorSnapshotCohort;
import org.opendaylight.controller.cluster.raft.RaftState;
import org.opendaylight.controller.cluster.raft.base.messages.FollowerInitialSyncUpStatus;
import org.opendaylight.controller.cluster.raft.messages.AppendEntriesReply;
+import org.opendaylight.controller.cluster.raft.messages.ServerRemoved;
import org.opendaylight.controller.cluster.raft.protobuff.client.messages.CompositeModificationByteStringPayload;
import org.opendaylight.controller.cluster.raft.protobuff.client.messages.CompositeModificationPayload;
-import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.TreeType;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import scala.concurrent.duration.Duration;
import scala.concurrent.duration.FiniteDuration;
@@ -79,14 +82,24 @@ import scala.concurrent.duration.FiniteDuration;
*
*/
public class Shard extends RaftActor {
-
- protected static final Object TX_COMMIT_TIMEOUT_CHECK_MESSAGE = "txCommitTimeoutCheck";
-
@VisibleForTesting
- static final Object GET_SHARD_MBEAN_MESSAGE = "getShardMBeanMessage";
+ static final Object TX_COMMIT_TIMEOUT_CHECK_MESSAGE = new Object() {
+ @Override
+ public String toString() {
+ return "txCommitTimeoutCheck";
+ }
+ };
@VisibleForTesting
- static final String DEFAULT_NAME = "default";
+ static final Object GET_SHARD_MBEAN_MESSAGE = new Object() {
+ @Override
+ public String toString() {
+ return "getShardMBeanMessage";
+ }
+ };
+
+ // FIXME: shard names should be encapsulated in their own class and this should be exposed as a constant.
+ public static final String DEFAULT_NAME = "default";
// The state of this Shard
private final ShardDataTree store;
@@ -115,19 +128,26 @@ public class Shard extends RaftActor {
private final DataTreeChangeListenerSupport treeChangeSupport = new DataTreeChangeListenerSupport(this);
private final DataChangeListenerSupport changeSupport = new DataChangeListenerSupport(this);
- protected Shard(final ShardIdentifier name, final Map peerAddresses,
- final DatastoreContext datastoreContext, final SchemaContext schemaContext) {
- super(name.toString(), new HashMap<>(peerAddresses), Optional.of(datastoreContext.getShardRaftConfig()),
- DataStoreVersions.CURRENT_VERSION);
- this.name = name.toString();
- this.datastoreContext = datastoreContext;
+ private ShardSnapshot restoreFromSnapshot;
+
+ private final ShardTransactionMessageRetrySupport messageRetrySupport;
+
+ protected Shard(AbstractBuilder, ?> builder) {
+ super(builder.getId().toString(), builder.getPeerAddresses(),
+ Optional.of(builder.getDatastoreContext().getShardRaftConfig()), DataStoreVersions.CURRENT_VERSION);
+
+ this.name = builder.getId().toString();
+ this.datastoreContext = builder.getDatastoreContext();
+ this.restoreFromSnapshot = builder.getRestoreFromSnapshot();
setPersistence(datastoreContext.isPersistent());
LOG.info("Shard created : {}, persistent : {}", name, datastoreContext.isPersistent());
- store = new ShardDataTree(schemaContext);
+ store = new ShardDataTree(builder.getSchemaContext(), builder.getTreeType(),
+ new ShardDataTreeChangeListenerPublisherActorProxy(getContext(), name + "-DTCL-publisher"),
+ new ShardDataChangeListenerPublisherActorProxy(getContext(), name + "-DCL-publisher"), name);
shardMBean = ShardMBeanFactory.getShardStatsMBean(name.toString(),
datastoreContext.getDataStoreMXBeanType());
@@ -139,7 +159,7 @@ public class Shard extends RaftActor {
commitCoordinator = new ShardCommitCoordinator(store,
datastoreContext.getShardCommitQueueExpiryTimeoutInMillis(),
- datastoreContext.getShardTransactionCommitQueueCapacity(), self(), LOG, this.name);
+ datastoreContext.getShardTransactionCommitQueueCapacity(), LOG, this.name);
setTransactionCommitTimeout();
@@ -155,7 +175,7 @@ public class Shard extends RaftActor {
snapshotCohort = new ShardSnapshotCohort(transactionActorFactory, store, LOG, this.name);
-
+ messageRetrySupport = new ShardTransactionMessageRetrySupport(this);
}
private void setTransactionCommitTimeout() {
@@ -163,11 +183,6 @@ public class Shard extends RaftActor {
datastoreContext.getShardTransactionCommitTimeoutInSeconds(), TimeUnit.SECONDS) / 2;
}
- public static Props props(final ShardIdentifier name, final Map peerAddresses,
- final DatastoreContext datastoreContext, final SchemaContext schemaContext) {
- return Props.create(new ShardCreator(name, peerAddresses, datastoreContext, schemaContext));
- }
-
private Optional createRoleChangeNotifier(String shardId) {
ActorRef shardRoleChangeNotifier = this.getContext().actorOf(
RoleChangeNotifier.getProps(shardId), shardId + "-notifier");
@@ -180,62 +195,52 @@ public class Shard extends RaftActor {
super.postStop();
+ messageRetrySupport.close();
+
if(txCommitTimeoutCheckSchedule != null) {
txCommitTimeoutCheckSchedule.cancel();
}
+ commitCoordinator.abortPendingTransactions("Transaction aborted due to shutdown.", this);
+
shardMBean.unregisterMBean();
}
@Override
- public void onReceiveRecover(final Object message) throws Exception {
- if(LOG.isDebugEnabled()) {
- LOG.debug("{}: onReceiveRecover: Received message {} from {}", persistenceId(),
- message.getClass().toString(), getSender());
- }
+ protected void handleRecover(final Object message) {
+ LOG.debug("{}: onReceiveRecover: Received message {} from {}", persistenceId(), message.getClass(),
+ getSender());
- if (message instanceof RecoveryFailure){
- LOG.error("{}: Recovery failed because of this cause",
- persistenceId(), ((RecoveryFailure) message).cause());
-
- // Even though recovery failed, we still need to finish our recovery, eg send the
- // ActorInitialized message and start the txCommitTimeoutCheckSchedule.
- onRecoveryComplete();
- } else {
- super.onReceiveRecover(message);
- if(LOG.isTraceEnabled()) {
- appendEntriesReplyTracker.begin();
- }
+ super.handleRecover(message);
+ if (LOG.isTraceEnabled()) {
+ appendEntriesReplyTracker.begin();
}
}
@Override
- public void onReceiveCommand(final Object message) throws Exception {
-
- MessageTracker.Context context = appendEntriesReplyTracker.received(message);
-
- if(context.error().isPresent()){
- LOG.trace("{} : AppendEntriesReply failed to arrive at the expected interval {}", persistenceId(),
- context.error());
- }
+ protected void handleNonRaftCommand(final Object message) {
+ try (final MessageTracker.Context context = appendEntriesReplyTracker.received(message)) {
+ final Optional maybeError = context.error();
+ if (maybeError.isPresent()) {
+ LOG.trace("{} : AppendEntriesReply failed to arrive at the expected interval {}", persistenceId(),
+ maybeError.get());
+ }
- try {
- if (CreateTransaction.SERIALIZABLE_CLASS.isInstance(message)) {
+ if (CreateTransaction.isSerializedType(message)) {
handleCreateTransaction(message);
- } else if (BatchedModifications.class.isInstance(message)) {
+ } else if (message instanceof BatchedModifications) {
handleBatchedModifications((BatchedModifications)message);
} else if (message instanceof ForwardedReadyTransaction) {
- commitCoordinator.handleForwardedReadyTransaction((ForwardedReadyTransaction) message,
- getSender(), this);
+ handleForwardedReadyTransaction((ForwardedReadyTransaction) message);
} else if (message instanceof ReadyLocalTransaction) {
handleReadyLocalTransaction((ReadyLocalTransaction)message);
- } else if (CanCommitTransaction.SERIALIZABLE_CLASS.isInstance(message)) {
+ } else if (CanCommitTransaction.isSerializedType(message)) {
handleCanCommitTransaction(CanCommitTransaction.fromSerializable(message));
- } else if (CommitTransaction.SERIALIZABLE_CLASS.isInstance(message)) {
+ } else if (CommitTransaction.isSerializedType(message)) {
handleCommitTransaction(CommitTransaction.fromSerializable(message));
- } else if (AbortTransaction.SERIALIZABLE_CLASS.isInstance(message)) {
+ } else if (AbortTransaction.isSerializedType(message)) {
handleAbortTransaction(AbortTransaction.fromSerializable(message));
- } else if (CloseTransactionChain.SERIALIZABLE_CLASS.isInstance(message)) {
+ } else if (CloseTransactionChain.isSerializedType(message)) {
closeTransactionChain(CloseTransactionChain.fromSerializable(message));
} else if (message instanceof RegisterChangeListener) {
changeSupport.onMessage((RegisterChangeListener) message, isLeader(), hasLeader());
@@ -247,8 +252,8 @@ public class Shard extends RaftActor {
PeerAddressResolved resolved = (PeerAddressResolved) message;
setPeerAddress(resolved.getPeerId().toString(),
resolved.getPeerAddress());
- } else if (message.equals(TX_COMMIT_TIMEOUT_CHECK_MESSAGE)) {
- handleTransactionCommitTimeoutCheck();
+ } else if (TX_COMMIT_TIMEOUT_CHECK_MESSAGE.equals(message)) {
+ commitCoordinator.checkForExpiredTransactions(transactionCommitTimeout, this);
} else if(message instanceof DatastoreContext) {
onDatastoreContext((DatastoreContext)message);
} else if(message instanceof RegisterRoleChangeListener){
@@ -258,13 +263,15 @@ public class Shard extends RaftActor {
context().parent().tell(message, self());
} else if(GET_SHARD_MBEAN_MESSAGE.equals(message)){
sender().tell(getShardMBean(), self());
- } else if(message instanceof GetShardDataTree){
+ } else if(message instanceof GetShardDataTree) {
sender().tell(store.getDataTree(), self());
+ } else if(message instanceof ServerRemoved){
+ context().parent().forward(message, context());
+ } else if(ShardTransactionMessageRetrySupport.TIMER_MESSAGE_CLASS.isInstance(message)) {
+ messageRetrySupport.onTimerMessage(message);
} else {
- super.onReceiveCommand(message);
+ super.handleNonRaftCommand(message);
}
- } finally {
- context.done();
}
}
@@ -276,6 +283,10 @@ public class Shard extends RaftActor {
return commitCoordinator.getQueueSize();
}
+ public int getCohortCacheSize() {
+ return commitCoordinator.getCohortCacheSize();
+ }
+
@Override
protected Optional getRoleChangeNotifier() {
return roleChangeNotifier;
@@ -283,9 +294,8 @@ public class Shard extends RaftActor {
@Override
protected LeaderStateChanged newLeaderStateChanged(String memberId, String leaderId, short leaderPayloadVersion) {
- return new ShardLeaderStateChanged(memberId, leaderId,
- isLeader() ? Optional.of(store.getDataTree()) : Optional.absent(),
- leaderPayloadVersion);
+ return isLeader() ? new ShardLeaderStateChanged(memberId, leaderId, store.getDataTree(), leaderPayloadVersion)
+ : new ShardLeaderStateChanged(memberId, leaderId, leaderPayloadVersion);
}
protected void onDatastoreContext(DatastoreContext context) {
@@ -304,20 +314,6 @@ public class Shard extends RaftActor {
updateConfigParams(datastoreContext.getShardRaftConfig());
}
- private void handleTransactionCommitTimeoutCheck() {
- CohortEntry cohortEntry = commitCoordinator.getCurrentCohortEntry();
- if(cohortEntry != null) {
- if(cohortEntry.isExpired(transactionCommitTimeout)) {
- LOG.warn("{}: Current transaction {} has timed out after {} ms - aborting",
- persistenceId(), cohortEntry.getTransactionID(), transactionCommitTimeout);
-
- doAbortTransaction(cohortEntry.getTransactionID(), null);
- }
- }
-
- commitCoordinator.cleanupExpiredCohortEntries();
- }
-
private static boolean isEmptyCommit(final DataTreeCandidate candidate) {
return ModificationType.UNMODIFIED.equals(candidate.getRootNode().getModificationType());
}
@@ -332,13 +328,24 @@ public class Shard extends RaftActor {
applyModificationToState(cohortEntry.getReplySender(), cohortEntry.getTransactionID(), candidate);
} else {
Shard.this.persistData(cohortEntry.getReplySender(), cohortEntry.getTransactionID(),
- DataTreeCandidatePayload.create(candidate));
+ DataTreeCandidatePayload.create(candidate));
}
}
private void handleCommitTransaction(final CommitTransaction commit) {
- if(!commitCoordinator.handleCommit(commit.getTransactionID(), getSender(), this)) {
- shardMBean.incrementFailedTransactionsCount();
+ if (isLeader()) {
+ if(!commitCoordinator.handleCommit(commit.getTransactionID(), getSender(), this)) {
+ shardMBean.incrementFailedTransactionsCount();
+ }
+ } else {
+ ActorSelection leader = getLeader();
+ if (leader == null) {
+ messageRetrySupport.addMessageToRetry(commit, getSender(),
+ "Could not commit transaction " + commit.getTransactionID());
+ } else {
+ LOG.debug("{}: Forwarding CommitTransaction to leader {}", persistenceId(), leader);
+ leader.forward(commit, getContext());
+ }
}
}
@@ -346,9 +353,29 @@ public class Shard extends RaftActor {
LOG.debug("{}: Finishing commit for transaction {}", persistenceId(), cohortEntry.getTransactionID());
try {
- cohortEntry.commit();
+ try {
+ cohortEntry.commit();
+ } catch(ExecutionException e) {
+ // We may get a "store tree and candidate base differ" IllegalStateException from commit under
+ // certain edge case scenarios so we'll try to re-apply the candidate from scratch as a last
+ // resort. Eg, we're a follower and a tx payload is replicated but the leader goes down before
+ // applying it to the state. We then become the leader and a second tx is pre-committed and
+ // replicated. When consensus occurs, this will cause the first tx to be applied as a foreign
+ // candidate via applyState prior to the second tx. Since the second tx has already been
+ // pre-committed, when it gets here to commit it will get an IllegalStateException.
+
+ // FIXME - this is not an ideal way to handle this scenario. This is temporary - a cleaner
+ // solution will be forthcoming.
+ if(e.getCause() instanceof IllegalStateException) {
+ LOG.debug("{}: commit failed for transaction {} - retrying as foreign candidate", persistenceId(),
+ transactionID, e);
+ store.applyForeignCandidate(transactionID, cohortEntry.getCandidate());
+ } else {
+ throw e;
+ }
+ }
- sender.tell(CommitTransactionReply.INSTANCE.toSerializable(), getSelf());
+ sender.tell(CommitTransactionReply.instance(cohortEntry.getClientVersion()).toSerializable(), getSelf());
shardMBean.incrementCommittedTransactionCount();
shardMBean.setLastCommittedTransactionTime(System.currentTimeMillis());
@@ -385,7 +412,8 @@ public class Shard extends RaftActor {
LOG.error("{}: Failed to re-apply transaction {}", persistenceId(), transactionID, e);
}
- sender.tell(CommitTransactionReply.INSTANCE.toSerializable(), getSelf());
+ sender.tell(CommitTransactionReply.instance(cohortEntry.getClientVersion()).toSerializable(),
+ getSelf());
} else {
// This really shouldn't happen - it likely means that persistence or replication
// took so long to complete such that the cohort entry was expired from the cache.
@@ -402,13 +430,19 @@ public class Shard extends RaftActor {
private void handleCanCommitTransaction(final CanCommitTransaction canCommit) {
LOG.debug("{}: Can committing transaction {}", persistenceId(), canCommit.getTransactionID());
- commitCoordinator.handleCanCommit(canCommit.getTransactionID(), getSender(), this);
- }
- private void noLeaderError(String errMessage, Object message) {
- // TODO: rather than throwing an immediate exception, we could schedule a timer to try again to make
- // it more resilient in case we're in the process of electing a new leader.
- getSender().tell(new akka.actor.Status.Failure(new NoShardLeaderException(errMessage, persistenceId())), getSelf());
+ if (isLeader()) {
+ commitCoordinator.handleCanCommit(canCommit.getTransactionID(), getSender(), this);
+ } else {
+ ActorSelection leader = getLeader();
+ if (leader == null) {
+ messageRetrySupport.addMessageToRetry(canCommit, getSender(),
+ "Could not canCommit transaction " + canCommit.getTransactionID());
+ } else {
+ LOG.debug("{}: Forwarding CanCommitTransaction to leader {}", persistenceId(), leader);
+ leader.forward(canCommit, getContext());
+ }
+ }
}
protected void handleBatchedModificationsLocal(BatchedModifications batched, ActorRef sender) {
@@ -434,20 +468,28 @@ public class Shard extends RaftActor {
// the primary/leader shard. However with timing and caching on the front-end, there's a small
// window where it could have a stale leader during leadership transitions.
//
- if(isLeader()) {
- failIfIsolatedLeader(getSender());
-
+ boolean isLeaderActive = isLeaderActive();
+ if (isLeader() && isLeaderActive) {
handleBatchedModificationsLocal(batched, getSender());
} else {
ActorSelection leader = getLeader();
- if(leader != null) {
- // TODO: what if this is not the first batch and leadership changed in between batched messages?
- // We could check if the commitCoordinator already has a cached entry and forward all the previous
- // batched modifications.
- LOG.debug("{}: Forwarding BatchedModifications to leader {}", persistenceId(), leader);
- leader.forward(batched, getContext());
+ if (!isLeaderActive || leader == null) {
+ messageRetrySupport.addMessageToRetry(batched, getSender(),
+ "Could not commit transaction " + batched.getTransactionID());
} else {
- noLeaderError("Could not commit transaction " + batched.getTransactionID(), batched);
+ // If this is not the first batch and leadership changed in between batched messages,
+ // we need to reconstruct previous BatchedModifications from the transaction
+ // DataTreeModification, honoring the max batched modification count, and forward all the
+ // previous BatchedModifications to the new leader.
+ Collection newModifications = commitCoordinator.createForwardedBatchedModifications(
+ batched, datastoreContext.getShardBatchedModificationCount());
+
+ LOG.debug("{}: Forwarding {} BatchedModifications to leader {}", persistenceId(),
+ newModifications.size(), leader);
+
+ for(BatchedModifications bm: newModifications) {
+ leader.forward(bm, getContext());
+ }
}
}
}
@@ -469,9 +511,10 @@ public class Shard extends RaftActor {
}
private void handleReadyLocalTransaction(final ReadyLocalTransaction message) {
- if (isLeader()) {
- failIfIsolatedLeader(getSender());
+ LOG.debug("{}: handleReadyLocalTransaction for {}", persistenceId(), message.getTransactionID());
+ boolean isLeaderActive = isLeaderActive();
+ if (isLeader() && isLeaderActive) {
try {
commitCoordinator.handleReadyLocalTransaction(message, getSender(), this);
} catch (Exception e) {
@@ -481,12 +524,35 @@ public class Shard extends RaftActor {
}
} else {
ActorSelection leader = getLeader();
- if (leader != null) {
+ if (!isLeaderActive || leader == null) {
+ messageRetrySupport.addMessageToRetry(message, getSender(),
+ "Could not commit transaction " + message.getTransactionID());
+ } else {
LOG.debug("{}: Forwarding ReadyLocalTransaction to leader {}", persistenceId(), leader);
message.setRemoteVersion(getCurrentBehavior().getLeaderPayloadVersion());
leader.forward(message, getContext());
+ }
+ }
+ }
+
+ private void handleForwardedReadyTransaction(ForwardedReadyTransaction forwardedReady) {
+ LOG.debug("{}: handleForwardedReadyTransaction for {}", persistenceId(), forwardedReady.getTransactionID());
+
+ boolean isLeaderActive = isLeaderActive();
+ if (isLeader() && isLeaderActive) {
+ commitCoordinator.handleForwardedReadyTransaction(forwardedReady, getSender(), this);
+ } else {
+ ActorSelection leader = getLeader();
+ if (!isLeaderActive || leader == null) {
+ messageRetrySupport.addMessageToRetry(forwardedReady, getSender(),
+ "Could not commit transaction " + forwardedReady.getTransactionID());
} else {
- noLeaderError("Could not commit transaction " + message.getTransactionID(), message);
+ LOG.debug("{}: Forwarding ForwardedReadyTransaction to leader {}", persistenceId(), leader);
+
+ ReadyLocalTransaction readyLocal = new ReadyLocalTransaction(forwardedReady.getTransactionID(),
+ forwardedReady.getTransaction().getSnapshot(), forwardedReady.isDoImmediateCommit());
+ readyLocal.setRemoteVersion(getCurrentBehavior().getLeaderPayloadVersion());
+ leader.forward(readyLocal, getContext());
}
}
}
@@ -515,11 +581,10 @@ public class Shard extends RaftActor {
}
private ActorRef createTypedTransactionActor(int transactionType,
- ShardTransactionIdentifier transactionId, String transactionChainId,
- short clientVersion ) {
+ ShardTransactionIdentifier transactionId, String transactionChainId) {
return transactionActorFactory.newShardTransaction(TransactionType.fromInt(transactionType),
- transactionId, transactionChainId, clientVersion);
+ transactionId, transactionChainId);
}
private void createTransaction(CreateTransaction createTransaction) {
@@ -530,18 +595,17 @@ public class Shard extends RaftActor {
}
ActorRef transactionActor = createTransaction(createTransaction.getTransactionType(),
- createTransaction.getTransactionId(), createTransaction.getTransactionChainId(),
- createTransaction.getVersion());
+ createTransaction.getTransactionId(), createTransaction.getTransactionChainId());
getSender().tell(new CreateTransactionReply(Serialization.serializedActorPath(transactionActor),
- createTransaction.getTransactionId()).toSerializable(), getSelf());
+ createTransaction.getTransactionId(), createTransaction.getVersion()).toSerializable(), getSelf());
} catch (Exception e) {
getSender().tell(new akka.actor.Status.Failure(e), getSelf());
}
}
private ActorRef createTransaction(int transactionType, String remoteTransactionId,
- String transactionChainId, short clientVersion) {
+ String transactionChainId) {
ShardTransactionIdentifier transactionId = new ShardTransactionIdentifier(remoteTransactionId);
@@ -551,7 +615,7 @@ public class Shard extends RaftActor {
}
ActorRef transactionActor = createTypedTransactionActor(transactionType, transactionId,
- transactionChainId, clientVersion);
+ transactionChainId);
return transactionActor;
}
@@ -592,11 +656,14 @@ public class Shard extends RaftActor {
@Override
@Nonnull
protected RaftActorRecoveryCohort getRaftActorRecoveryCohort() {
- return new ShardRecoveryCoordinator(store, store.getSchemaContext(), persistenceId(), LOG);
+ return new ShardRecoveryCoordinator(store, store.getSchemaContext(),
+ restoreFromSnapshot != null ? restoreFromSnapshot.getSnapshot() : null, persistenceId(), LOG);
}
@Override
protected void onRecoveryComplete() {
+ restoreFromSnapshot = null;
+
//notify shard manager
getContext().parent().tell(new ActorInitialized(), getSelf());
@@ -625,12 +692,6 @@ public class Shard extends RaftActor {
// Replication consensus reached, proceed to commit
finishCommit(clientActor, identifier);
}
- } else if (data instanceof ModificationPayload) {
- try {
- applyModificationToState(clientActor, identifier, ((ModificationPayload) data).getModification());
- } catch (ClassNotFoundException | IOException e) {
- LOG.error("{}: Error extracting ModificationPayload", persistenceId(), e);
- }
} else if (data instanceof CompositeModificationPayload) {
Object modification = ((CompositeModificationPayload) data).getModification();
@@ -678,11 +739,49 @@ public class Shard extends RaftActor {
store.closeAllTransactionChains();
}
+
+ if(hasLeader && !isIsolatedLeader()) {
+ messageRetrySupport.retryMessages();
+ }
}
@Override
protected void onLeaderChanged(String oldLeader, String newLeader) {
shardMBean.incrementLeadershipChangeCount();
+
+ boolean hasLeader = hasLeader();
+ if(hasLeader && !isLeader()) {
+ // Another leader was elected. If we were the previous leader and had pending transactions, convert
+ // them to transaction messages and send to the new leader.
+ ActorSelection leader = getLeader();
+ if(leader != null) {
+ Collection