Permalink
Browse files

Writing unit tests to cover cluster member retry management

  • Loading branch information...
John Hopper
John Hopper committed Mar 7, 2012
1 parent 6d38cfa commit c09da9972a4adc3f8c78952e77e1e66fcc666da5
@@ -4,63 +4,70 @@
public class ClusterMember {
- private final static int REQUIRED_VALIDATION_PASSES = 4;
-
- private final InetSocketAddress memberAddress;
- private final int droppedMemberRestTime;
-
- private long droppedTime, restPeriod;
- private int validationPass;
- private boolean online;
-
- public ClusterMember(InetSocketAddress memberAddress, int droppedMemberRestTime) {
- this.memberAddress = memberAddress;
- this.droppedMemberRestTime = droppedMemberRestTime;
-
- online = true;
- validationPass = 0;
- }
-
- private static long nowInMilliseconds() {
- return System.currentTimeMillis();
- }
-
- public InetSocketAddress getMemberAddress() {
- return memberAddress;
- }
-
- public boolean shouldRetry() {
- final long now = nowInMilliseconds();
- final boolean retry = now - droppedTime > restPeriod;
-
- if (retry) {
- if (validationPass++ <= REQUIRED_VALIDATION_PASSES) {
- restPeriod = droppedMemberRestTime / validationPass;
- droppedTime = now;
- } else {
- validationPass = 0;
- droppedTime = 0;
-
- online = true;
- }
- }
-
- return retry;
- }
-
- public void setOffline() {
- droppedTime = nowInMilliseconds();
- restPeriod = droppedMemberRestTime;
- validationPass = 0;
-
- online = false;
- }
-
- public boolean isOnline() {
- return online;
- }
-
- public boolean isOffline() {
- return !online;
- }
+ private final static int REQUIRED_VALIDATION_PASSES = 4;
+ private final InetSocketAddress memberAddress;
+ private final int droppedMemberRestTime, requiredValidationPasses;
+ private long droppedTime, restPeriod;
+ private int validationPass;
+ private boolean online;
+
+ public ClusterMember(InetSocketAddress memberAddress, int droppedMemberRestTime) {
+ this(REQUIRED_VALIDATION_PASSES, memberAddress, droppedMemberRestTime);
+ }
+
+ public ClusterMember(int requiredValidationPasses, InetSocketAddress memberAddress, int droppedMemberRestTime) {
+ this.memberAddress = memberAddress;
+ this.droppedMemberRestTime = droppedMemberRestTime;
+ this.requiredValidationPasses = requiredValidationPasses;
+
+ online = true;
+ validationPass = 0;
+ }
+
+ private static long nowInMilliseconds() {
+ return System.currentTimeMillis();
+ }
+
+ public InetSocketAddress getMemberAddress() {
+ return memberAddress;
+ }
+
+ public boolean shouldRetry() {
+ final long nowInMilliseconds = nowInMilliseconds();
+ final boolean retry = nowInMilliseconds - droppedTime > restPeriod;
+
+ if (retry) {
+ logMemberRetry(nowInMilliseconds);
+ }
+
+ return retry;
+ }
+
+ private void logMemberRetry(long nowInMilliseconds) {
+ if (validationPass++ < requiredValidationPasses) {
+ restPeriod = droppedMemberRestTime / validationPass;
+ droppedTime = nowInMilliseconds;
+ } else {
+ validationPass = 0;
+ droppedTime = 0;
+
+ online = true;
+ }
+ }
+
+ public void setOffline() {
+ droppedTime = nowInMilliseconds();
+ restPeriod = droppedMemberRestTime;
+ validationPass = 0;
+
+ online = false;
+ }
+
+ public boolean isOnline() {
+ return online;
+ }
+
+ public boolean isOffline() {
+ return !online;
+ }
}
@@ -0,0 +1,100 @@
+package com.rackspace.papi.service.datastore.cluster.member;
+
+import java.net.InetSocketAddress;
+import org.junit.Test;
+import org.junit.experimental.runners.Enclosed;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+
+/**
+ * I love well composed code just as much as I love testing it.
+ *
+ * @author zinic
+ */
+@RunWith(Enclosed.class)
+public class ClusterMemberTest {
+
+ public static class WhenMarkingClusterMembersOffline {
+
+ @Test
+ public void shouldMarkMemberOffline() {
+ final InetSocketAddress address = mock(InetSocketAddress.class);
+ final ClusterMember clusterMember = new ClusterMember(address, 10);
+
+ assertTrue("A cluster member must start in an online state", !clusterMember.isOffline() && clusterMember.isOnline());
+
+ clusterMember.setOffline();
+
+ assertTrue("A cluster member that's been marked as offline must communicate its offline state", clusterMember.isOffline() && !clusterMember.isOnline());
+ }
+
+ @Test
+ public void shouldNotAllowRetryBeforeMemberCooldown() {
+ final InetSocketAddress address = mock(InetSocketAddress.class);
+ final ClusterMember clusterMember = new ClusterMember(address, 1000);
+
+ clusterMember.setOffline();
+
+ assertFalse("A cluster member that has not been offline for the duration of its rest period must not be eligible for retry", clusterMember.shouldRetry());
+ }
+
+ @Test
+ public void shouldAllowRetryAfterMemberCooldown() {
+ final InetSocketAddress address = mock(InetSocketAddress.class);
+ final ClusterMember clusterMember = new ClusterMember(address, 1);
+
+ clusterMember.setOffline();
+
+ sleep(5);
+
+ assertTrue("A cluster member that's been offline for the duration of its rest period should be eligible for retry", clusterMember.shouldRetry());
+ assertTrue("A cluster member that's eligible for retry should still be marked as offline when it has not finished all validation passes", clusterMember.isOffline() && !clusterMember.isOnline());
+ }
+
+ @Test
+ public void shouldResetValidationPasses() {
+ final InetSocketAddress address = mock(InetSocketAddress.class);
+ final ClusterMember clusterMember = new ClusterMember(1, address, 1);
+
+ clusterMember.setOffline();
+
+ sleep(5);
+
+ assertTrue("A cluster member that's been offline for the duration of its rest period should be eligible for retry", clusterMember.shouldRetry());
+
+ clusterMember.setOffline();
+
+ sleep(5);
+
+ assertTrue("A cluster member that's been offline for the duration of its rest period should be eligible for retry", clusterMember.shouldRetry());
+ assertTrue("A cluster member that's eligible for retry should still be marked as offline when it has not finished all validation passes", clusterMember.isOffline() && !clusterMember.isOnline());
+ }
+
+ @Test
+ public void shouldOnlineMemberAfterMemberCompletesAllValidationPasses() {
+ final InetSocketAddress address = mock(InetSocketAddress.class);
+ final ClusterMember clusterMember = new ClusterMember(1, address, 1);
+
+ clusterMember.setOffline();
+
+ sleep(5);
+
+ assertTrue("A cluster member that's been offline for the duration of its rest period should be eligible for retry", clusterMember.shouldRetry());
+
+ sleep(5);
+
+ assertTrue("A cluster member that's been offline for the duration of its rest period should be eligible for retry", clusterMember.shouldRetry());
+ assertTrue("A cluster member that's eligible for retry and has completed all of its validation passes should still be marked as online", clusterMember.isOnline() && !clusterMember.isOffline());
+ }
+ }
+
+ public static void sleep(int miliseconds) {
+ try {
+ Thread.sleep(miliseconds);
+ } catch (InterruptedException ie) {
+ fail("Thread was interrupted - unable to complete test.");
+ }
+ }
+}

0 comments on commit c09da99

Please sign in to comment.