Better handling of zk bad version

- Remove propagating BadVersionException from ZookeeperStateMachineEnsemble
  via event so that machine can be kept running. Further logs are then read
  anyway via watcher. Thought we still need better handling but this
  is better for now.
- Relates to #92
This commit is contained in:
Janne Valkealahti
2015-08-07 11:11:46 +01:00
parent e6c400e31c
commit 4008436d75
4 changed files with 111 additions and 20 deletions

View File

@@ -18,6 +18,8 @@ package org.springframework.statemachine.test;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.empty;
import static org.junit.Assert.assertThat;
import java.util.ArrayList;
@@ -112,15 +114,19 @@ public class StateMachineTestPlan<S, E> {
}
if (step.sendEvent != null) {
StateMachine<S, E> sendVia = null;
ArrayList<StateMachine<S, E>> sendVia = new ArrayList<StateMachine<S, E>>();
if (step.sendEventMachineId != null) {
sendVia = stateMachines.get(step.sendEventMachineId);
sendVia.add(stateMachines.get(step.sendEventMachineId));
} else if (step.sendEventToAll) {
sendVia.addAll(stateMachines.values());
} else {
sendVia = stateMachines.values().iterator().next();
sendVia.add(stateMachines.values().iterator().next());
}
assertThat("Error finding machine to send via", sendVia, not(empty()));
for (StateMachine<S, E> machine : sendVia) {
log.info("Sending test event " + step.sendEvent + " via machine " + machine);
machine.sendEvent(step.sendEvent);
}
assertThat("Error finding machine to send via", sendVia, notNullValue());
log.info("Sending test event " + step.sendEvent + " via machine " + sendVia);
sendVia.sendEvent(step.sendEvent);
}
if (step.expectStateChanged != null) {

View File

@@ -111,6 +111,7 @@ public class StateMachineTestPlanBuilder<S, E> {
E sendEvent;
Object sendEventMachineId;
boolean sendEventToAll = false;
final Collection<S> expectStates = new ArrayList<S>();
Integer expectStateChanged;
Integer expectStateEntered;
@@ -156,7 +157,22 @@ public class StateMachineTestPlanBuilder<S, E> {
* @return the state machine test plan step builder
*/
public StateMachineTestPlanStepBuilder sendEvent(E event) {
return sendEvent(event, null);
return sendEvent(event, false);
}
/**
* Send an event {@code E}. If {@code sendToAll} is set to {@code TRUE} event
* will be send to all existing machines.
*
* @param event the event
* @param sendToAll send to all machines
* @return the state machine test plan step builder
*/
public StateMachineTestPlanStepBuilder sendEvent(E event, boolean sendToAll) {
this.sendEvent = event;
this.sendEventMachineId = null;
this.sendEventToAll = sendToAll;
return this;
}
/**
@@ -330,7 +346,7 @@ public class StateMachineTestPlanBuilder<S, E> {
* @return the state machine test plan builder for chaining
*/
public StateMachineTestPlanBuilder<S, E> and() {
steps.add(new StateMachineTestPlanStep<S, E>(sendEvent, sendEventMachineId, expectStates,
steps.add(new StateMachineTestPlanStep<S, E>(sendEvent, sendEventMachineId, sendEventToAll, expectStates,
expectStateChanged, expectStateEntered, expectStateExited, expectEventNotAccepted,
expectTransition, expectTransitionStarted, expectTransitionEnded, expectStateMachineStarted,
expectStateMachineStopped, expectVariableKeys, expectVariables));
@@ -342,6 +358,7 @@ public class StateMachineTestPlanBuilder<S, E> {
static class StateMachineTestPlanStep<S, E> {
E sendEvent;
Object sendEventMachineId;
boolean sendEventToAll = false;
final Collection<S> expectStates;
Integer expectStateChanged;
Integer expectStateEntered;
@@ -355,13 +372,15 @@ public class StateMachineTestPlanBuilder<S, E> {
final Collection<Object> expectVariableKeys;
final Map<Object, Object> expectVariables;
public StateMachineTestPlanStep(E sendEvent, Object sendEventMachineId, Collection<S> expectStates, Integer expectStateChanged,
Integer expectStateEntered, Integer expectStateExited, Integer expectEventNotAccepted,
Integer expectTransition, Integer expectTransitionStarted, Integer expectTransitionEnded,
Integer expectStateMachineStarted, Integer expectStateMachineStopped,
Collection<Object> expectVariableKeys, Map<Object, Object> expectVariables) {
public StateMachineTestPlanStep(E sendEvent, Object sendEventMachineId, boolean sendEventToAll,
Collection<S> expectStates, Integer expectStateChanged, Integer expectStateEntered,
Integer expectStateExited, Integer expectEventNotAccepted, Integer expectTransition,
Integer expectTransitionStarted, Integer expectTransitionEnded, Integer expectStateMachineStarted,
Integer expectStateMachineStopped, Collection<Object> expectVariableKeys,
Map<Object, Object> expectVariables) {
this.sendEvent = sendEvent;
this.sendEventMachineId = sendEventMachineId;
this.sendEventToAll = sendEventToAll;
this.expectStates = expectStates;
this.expectStateChanged = expectStateChanged;
this.expectStateEntered = expectStateEntered;

View File

@@ -32,7 +32,6 @@ import org.apache.curator.framework.imps.CuratorFrameworkState;
import org.apache.curator.framework.recipes.locks.InterProcessSemaphoreMutex;
import org.apache.curator.framework.recipes.nodes.PersistentEphemeralNode;
import org.apache.curator.framework.recipes.nodes.PersistentEphemeralNode.Mode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.data.Stat;
import org.springframework.statemachine.StateMachine;
@@ -189,12 +188,6 @@ public class ZookeeperStateMachineEnsemble<S, E> extends StateMachineEnsembleObj
persist.write(context, stat);
stateRef.set(new StateWrapper(context, stat.getVersion()));
} catch (Exception e) {
if (e instanceof StateMachineException) {
if (((StateMachineException)e).contains(KeeperException.BadVersionException.class)) {
notifyError(new StateMachineEnsembleException("Cas error during write id=[" + uuid
+ "] for context=[" + context + "]", e));
}
}
throw new StateMachineException("Error persisting data", e);
}
}

View File

@@ -257,6 +257,51 @@ public class ZookeeperStateMachineTests extends AbstractZookeeperTests {
plan.test();
}
@Test
public void testShouldHaveCasErrorDoesNotBreakMachines() throws Exception {
context.register(ZkServerConfig.class, BaseConfig.class);
context.refresh();
CuratorFramework curatorClient =
context.getBean("curatorClient", CuratorFramework.class);
StateMachine<String, String> machine1 =
buildTestStateMachine2(curatorClient);
StateMachine<String, String> machine2 =
buildTestStateMachine2(curatorClient);
StateMachine<String, String> machine3 =
buildTestStateMachine2(curatorClient);
StateMachine<String, String> machine4 =
buildTestStateMachine2(curatorClient);
StateMachine<String, String> machine5 =
buildTestStateMachine2(curatorClient);
StateMachineTestPlan<String, String> plan =
StateMachineTestPlanBuilder.<String, String>builder()
.defaultAwaitTime(2)
.stateMachine(machine1)
.stateMachine(machine2)
.stateMachine(machine3)
.stateMachine(machine4)
.stateMachine(machine5)
.step()
.expectStates("SI")
.and()
.step()
.sendEvent("E1", true)
.expectStateChanged(1)
.expectStates("S1")
.and()
.step()
.sendEvent("E2", true)
.expectStateChanged(1)
.expectStates("S2")
.and()
.build();
plan.test();
}
@Test
@SuppressWarnings("unchecked")
public void testJoinLaterShouldSyncState() throws Exception {
@@ -550,6 +595,34 @@ public class ZookeeperStateMachineTests extends AbstractZookeeperTests {
return builder.build();
}
private StateMachine<String, String> buildTestStateMachine2(CuratorFramework curatorClient)
throws Exception {
StateMachineBuilder.Builder<String, String> builder = StateMachineBuilder.builder();
builder.configureConfiguration()
.withConfiguration()
.taskExecutor(new SyncTaskExecutor())
.autoStartup(true)
.and()
.withDistributed()
.ensemble(stateMachineEnsemble(curatorClient));
builder.configureStates()
.withStates()
.initial("SI")
.state("S1")
.state("S2");
builder.configureTransitions()
.withExternal()
.source("SI").target("S1").event("E1")
.and()
.withExternal()
.source("S1").target("S2").event("E2");
return builder.build();
}
private static FooGuard foo0Guard() {
return new FooGuard(0);
}