*/
package org.opendaylight.controller.cluster.raft;
+import org.opendaylight.controller.cluster.raft.policy.RaftPolicy;
import scala.concurrent.duration.FiniteDuration;
/**
*/
long getElectionTimeoutFactor();
+
+ /**
+ *
+ * @return An instance of org.opendaylight.controller.cluster.raft.policy.RaftPolicy or an instance of the
+ * DefaultRaftPolicy
+ */
+ RaftPolicy getRaftPolicy();
}
*/
package org.opendaylight.controller.cluster.raft;
+import com.google.common.base.Strings;
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
import java.util.concurrent.TimeUnit;
+import org.opendaylight.controller.cluster.raft.policy.DefaultRaftPolicy;
+import org.opendaylight.controller.cluster.raft.policy.RaftPolicy;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import scala.concurrent.duration.FiniteDuration;
/**
*/
public class DefaultConfigParamsImpl implements ConfigParams {
+ private static final Logger LOG = LoggerFactory.getLogger(DefaultConfigParamsImpl.class);
+
private static final int SNAPSHOT_BATCH_COUNT = 20000;
private static final int JOURNAL_RECOVERY_LOG_BATCH_SIZE = 1000;
private int snaphotChunkSize = SNAPSHOT_CHUNK_SIZE;
private long electionTimeoutFactor = 2;
+ private String customRaftPolicyImplementationClass;
+
+ Supplier<RaftPolicy> policySupplier = Suppliers.memoize(new PolicySupplier());
public void setHeartBeatInterval(FiniteDuration heartBeatInterval) {
this.heartBeatInterval = heartBeatInterval;
electionTimeOutInterval = null;
}
+ public void setCustomRaftPolicyImplementationClass(String customRaftPolicyImplementationClass){
+ this.customRaftPolicyImplementationClass = customRaftPolicyImplementationClass;
+ }
+
@Override
public long getSnapshotBatchCount() {
return snapshotBatchCount;
public long getElectionTimeoutFactor() {
return electionTimeoutFactor;
}
+
+ @Override
+ public RaftPolicy getRaftPolicy() {
+ return policySupplier.get();
+ }
+
+ private class PolicySupplier implements Supplier<RaftPolicy>{
+ @Override
+ public RaftPolicy get() {
+ if(Strings.isNullOrEmpty(DefaultConfigParamsImpl.this.customRaftPolicyImplementationClass)){
+ return DefaultRaftPolicy.INSTANCE;
+ }
+ try {
+ String className = DefaultConfigParamsImpl.this.customRaftPolicyImplementationClass;
+ Class c = Class.forName(className);
+ RaftPolicy obj = (RaftPolicy)c.newInstance();
+ return obj;
+ } catch (Exception e) {
+ if(LOG.isDebugEnabled()) {
+ LOG.error("Could not create custom raft policy, will stick with default", e);
+ } else {
+ LOG.error("Could not create custom raft policy, will stick with default : cause = {}", e.getMessage());
+ }
+ }
+ return DefaultRaftPolicy.INSTANCE;
+ }
+ }
}
import com.google.common.base.Supplier;
import java.util.Map;
import org.opendaylight.controller.cluster.DataPersistenceProvider;
-import org.opendaylight.controller.cluster.raft.policy.DefaultRaftPolicy;
import org.opendaylight.controller.cluster.raft.policy.RaftPolicy;
import org.slf4j.Logger;
@Override
public RaftPolicy getRaftPolicy() {
- return DefaultRaftPolicy.INSTANCE;
+ return configParams.getRaftPolicy();
}
}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.cluster.raft;
+
+import static org.junit.Assert.assertEquals;
+import org.junit.Test;
+import org.opendaylight.controller.cluster.raft.policy.DefaultRaftPolicy;
+import org.opendaylight.controller.cluster.raft.policy.RaftPolicy;
+import org.opendaylight.controller.cluster.raft.policy.TestRaftPolicy;
+
+public class DefaultConfigParamsImplTest {
+
+ @Test
+ public void testGetRaftPolicyWithDefault(){
+ DefaultConfigParamsImpl params = new DefaultConfigParamsImpl();
+
+ assertEquals("Default instance", DefaultRaftPolicy.INSTANCE, params.getRaftPolicy());
+ }
+
+ @Test
+ public void testGetRaftPolicyInvalidClassName(){
+ DefaultConfigParamsImpl params = new DefaultConfigParamsImpl();
+ params.setCustomRaftPolicyImplementationClass("foobar");
+
+ assertEquals("Default instance", DefaultRaftPolicy.INSTANCE, params.getRaftPolicy());
+ }
+
+ @Test
+ public void testGetRaftPolicyValidClassNameButInvalidType(){
+ DefaultConfigParamsImpl params = new DefaultConfigParamsImpl();
+ params.setCustomRaftPolicyImplementationClass("java.lang.String");
+
+ assertEquals("Default instance", DefaultRaftPolicy.INSTANCE, params.getRaftPolicy());
+ }
+
+ @Test
+ public void testGetRaftPolicyValidClass(){
+ DefaultConfigParamsImpl params1 = new DefaultConfigParamsImpl();
+ params1.setCustomRaftPolicyImplementationClass("org.opendaylight.controller.cluster.raft.policy.TestRaftPolicy");
+ RaftPolicy behavior1 = params1.getRaftPolicy();
+
+ assertEquals("TestCustomBehavior", TestRaftPolicy.class, behavior1.getClass());
+ assertEquals("Same instance returned", behavior1, params1.getRaftPolicy());
+
+ DefaultConfigParamsImpl params2 = new DefaultConfigParamsImpl();
+ RaftPolicy behavior2 = params2.getRaftPolicy();
+ params1.setCustomRaftPolicyImplementationClass("org.opendaylight.controller.cluster.raft.policy.TestRaftPolicy");
+
+ assertEquals("Default instance", DefaultRaftPolicy.INSTANCE, behavior2);
+ assertEquals("Default instance", DefaultRaftPolicy.INSTANCE, params2.getRaftPolicy());
+
+ }
+
+
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.raft.policy;
+
+public class TestRaftPolicy implements RaftPolicy {
+
+ @Override
+ public boolean automaticElectionsEnabled() {
+ return false;
+ }
+
+ @Override
+ public boolean applyModificationToStateBeforeConsensus() {
+ return false;
+ }
+}
# The maximum queue size for each shard's data store executor.
#max-shard-data-store-executor-queue-size=5000
+# A fully qualified java class name. The class should implement
+# org.opendaylight.controller.cluster.raft.policy.RaftPolicy. This java class should be
+# accessible to the distributed data store OSGi module so that it can be dynamically loaded via
+# reflection. For now let's assume that these classes to customize raft behaviors should be
+# present in the distributed data store module itself. If this property is set to a class which
+# cannot be found then the default raft policy will be applied
+#custom-raft-policy-implementation=
+
private boolean writeOnlyTransactionOptimizationsEnabled = true;
private long shardCommitQueueExpiryTimeoutInMillis = DEFAULT_SHARD_COMMIT_QUEUE_EXPIRY_TIMEOUT_IN_MS;
private boolean transactionDebugContextEnabled = false;
+ private String customRaftPolicyImplementation = "";
public static Set<String> getGlobalDatastoreTypes() {
return globalDatastoreTypes;
this.writeOnlyTransactionOptimizationsEnabled = other.writeOnlyTransactionOptimizationsEnabled;
this.shardCommitQueueExpiryTimeoutInMillis = other.shardCommitQueueExpiryTimeoutInMillis;
this.transactionDebugContextEnabled = other.transactionDebugContextEnabled;
+ this.customRaftPolicyImplementation = other.customRaftPolicyImplementation;
setShardJournalRecoveryLogBatchSize(other.raftConfig.getJournalRecoveryLogBatchSize());
setSnapshotBatchCount(other.raftConfig.getSnapshotBatchCount());
setIsolatedLeaderCheckInterval(other.raftConfig.getIsolatedCheckIntervalInMillis());
setSnapshotDataThresholdPercentage(other.raftConfig.getSnapshotDataThresholdPercentage());
setElectionTimeoutFactor(other.raftConfig.getElectionTimeoutFactor());
+ setCustomRaftPolicyImplementation(other.customRaftPolicyImplementation);
+
}
public static Builder newBuilder() {
raftConfig.setElectionTimeoutFactor(shardElectionTimeoutFactor);
}
+ private void setCustomRaftPolicyImplementation(String customRaftPolicyImplementation) {
+ raftConfig.setCustomRaftPolicyImplementationClass(customRaftPolicyImplementation);
+ }
+
+
private void setSnapshotDataThresholdPercentage(int shardSnapshotDataThresholdPercentage) {
raftConfig.setSnapshotDataThresholdPercentage(shardSnapshotDataThresholdPercentage);
}
return datastoreContext;
}
+
+ public Builder customRaftPolicyImplementation(String customRaftPolicyImplementation) {
+ datastoreContext.setCustomRaftPolicyImplementation(customRaftPolicyImplementation);
+ return this;
+ }
}
}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore.policy;
+
+
+import org.opendaylight.controller.cluster.raft.policy.RaftPolicy;
+
+/**
+ * A RaftPolicy that disables elections so that we can then specify exactly which Shard Replica should be
+ * Leader. Once a Leader is assigned it will behave as per Raft.
+ */
+public class TestOnlyRaftPolicy implements RaftPolicy {
+ @Override
+ public boolean automaticElectionsEnabled() {
+ return false;
+ }
+
+ @Override
+ public boolean applyModificationToStateBeforeConsensus() {
+ return false;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore.policy;
+
+import org.opendaylight.controller.cluster.raft.policy.RaftPolicy;
+
+/**
+ * The TwoNodeClusterRaftPolicy is intended to be used in a two node deployment where when one instance
+ * of the controller goes down the other instance is to take over and move the state forward.
+ * When a TwoNodeClusterRaftPolicy is used Raft elections are disabled. This is primarily because we would
+ * need to specify the leader externally. Also since we want one node to continue to function while the other
+ * node is down we would need to apply a modification to the state before consensus occurs.
+ */
+public class TwoNodeClusterRaftPolicy implements RaftPolicy {
+ @Override
+ public boolean automaticElectionsEnabled() {
+ return false;
+ }
+
+ @Override
+ public boolean applyModificationToStateBeforeConsensus() {
+ return true;
+ }
+}
.shardCommitQueueExpiryTimeoutInSeconds(
props.getShardCommitQueueExpiryTimeoutInSeconds().getValue().intValue())
.transactionDebugContextEnabled(props.getTransactionDebugContextEnabled())
+ .customRaftPolicyImplementation(props.getCustomRaftPolicyImplementation())
.build();
return DistributedDataStoreFactory.createInstance(getConfigSchemaServiceDependency(),
props.getShardTransactionCommitQueueCapacity().getValue().intValue())
.persistent(props.getPersistent().booleanValue())
.shardIsolatedLeaderCheckIntervalInMillis(
- props.getShardIsolatedLeaderCheckIntervalInMillis().getValue())
+ props.getShardIsolatedLeaderCheckIntervalInMillis().getValue())
.shardElectionTimeoutFactor(props.getShardElectionTimeoutFactor().getValue())
.transactionCreationInitialRateLimit(props.getTransactionCreationInitialRateLimit().getValue())
.shardBatchedModificationCount(props.getShardBatchedModificationCount().getValue().intValue())
.shardCommitQueueExpiryTimeoutInSeconds(
props.getShardCommitQueueExpiryTimeoutInSeconds().getValue().intValue())
.transactionDebugContextEnabled(props.getTransactionDebugContextEnabled())
+ .customRaftPolicyImplementation(props.getCustomRaftPolicyImplementation())
.build();
return DistributedDataStoreFactory.createInstance(getOperationalSchemaServiceDependency(),
description "Enable or disable transaction context debug. This will log the call site trace for
transactions that fail";
}
+
+ leaf custom-raft-policy-implementation {
+ default "";
+ type string;
+ description "A fully qualified java class name. The class should implement
+ org.opendaylight.controller.cluster.raft.policy.RaftPolicy. This java class should be
+ accessible to the distributed data store OSGi module so that it can be dynamically loaded via
+ reflection. For now let's assume that these classes to customize raft behaviors should be
+ present in the distributed data store module itself. If this property is set to a class which
+ cannot be found then the default raft behavior will be applied";
+ }
}
// Augments the 'configuration' choice node under modules/module.