mirror of
https://github.com/Graylog2/graylog2-server.git
synced 2026-03-13 09:32:21 +08:00
Process watchdog reimplemented to follow transitions of the process state machine (#15676)
* Process watchdog reimplemented to follow transitions of the process state machine * code cleanup * code cleanup
This commit is contained in:
@@ -23,6 +23,7 @@ import com.github.rholder.retry.StopStrategies;
|
||||
import com.github.rholder.retry.WaitStrategies;
|
||||
import org.apache.commons.exec.ExecuteException;
|
||||
import org.assertj.core.api.Assertions;
|
||||
import org.graylog.datanode.process.ProcessEvent;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Graylog, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the Server Side Public License, version 1,
|
||||
* as published by MongoDB, Inc.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* Server Side Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the Server Side Public License
|
||||
* along with this program. If not, see
|
||||
* <http://www.mongodb.com/licensing/server-side-public-license>.
|
||||
*/
|
||||
package org.graylog.datanode.management;
|
||||
|
||||
import com.github.rholder.retry.RetryException;
|
||||
import com.github.rholder.retry.Retryer;
|
||||
import com.github.rholder.retry.RetryerBuilder;
|
||||
import com.github.rholder.retry.StopStrategies;
|
||||
import com.github.rholder.retry.WaitStrategies;
|
||||
import org.assertj.core.api.Assertions;
|
||||
import org.graylog.datanode.process.ProcessState;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
class ProcessWatchdogTest {
|
||||
|
||||
|
||||
@Test
|
||||
void testLifecycle() throws IOException, ExecutionException, RetryException {
|
||||
final TestableProcess process = new TestableProcess();
|
||||
final ProcessWatchdog watchdog = new ProcessWatchdog(process, 100);
|
||||
process.restart();
|
||||
watchdog.start();
|
||||
|
||||
// both process and watchdog are running now. Let's stop the process and see if the watchdog will restart it
|
||||
process.stop();
|
||||
|
||||
// see if the process is starting again
|
||||
Assertions.assertThat(isInStartingState(process)).isTrue();
|
||||
|
||||
// repeat
|
||||
process.stop();
|
||||
Assertions.assertThat(isInStartingState(process)).isTrue();
|
||||
|
||||
process.stop();
|
||||
Assertions.assertThat(isInStartingState(process)).isTrue();
|
||||
|
||||
// this is the 4th termination, we give up trying
|
||||
process.stop();
|
||||
|
||||
Assertions.assertThat(isWatchdogTerminated(watchdog)).isTrue();
|
||||
Assertions.assertThat(process.isInState(ProcessState.TERMINATED)).isTrue();
|
||||
}
|
||||
|
||||
private boolean isInStartingState(TestableProcess process) throws ExecutionException, RetryException {
|
||||
final Retryer<Boolean> retryer = RetryerBuilder.<Boolean>newBuilder()
|
||||
.withWaitStrategy(WaitStrategies.fixedWait(100, TimeUnit.MILLISECONDS))
|
||||
.withStopStrategy(StopStrategies.stopAfterAttempt(20))
|
||||
.retryIfResult(Boolean.FALSE::equals)
|
||||
.build();
|
||||
|
||||
return retryer.call(() -> process.isInState(ProcessState.STARTING));
|
||||
}
|
||||
|
||||
private boolean isWatchdogTerminated(ProcessWatchdog watchdog) throws ExecutionException, RetryException {
|
||||
final Retryer<Boolean> retryer = RetryerBuilder.<Boolean>newBuilder()
|
||||
.withWaitStrategy(WaitStrategies.fixedWait(100, TimeUnit.MILLISECONDS))
|
||||
.withStopStrategy(StopStrategies.stopAfterAttempt(5))
|
||||
.retryIfResult(Boolean.FALSE::equals)
|
||||
.build();
|
||||
|
||||
return retryer.call(watchdog::isStopped);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Graylog, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the Server Side Public License, version 1,
|
||||
* as published by MongoDB, Inc.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* Server Side Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the Server Side Public License
|
||||
* along with this program. If not, see
|
||||
* <http://www.mongodb.com/licensing/server-side-public-license>.
|
||||
*/
|
||||
package org.graylog.datanode.management;
|
||||
|
||||
import org.assertj.core.api.Assertions;
|
||||
import org.graylog.datanode.process.ProcessEvent;
|
||||
import org.graylog.datanode.process.ProcessState;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class ProcessWatchdogTracerTest {
|
||||
|
||||
|
||||
@Test
|
||||
void testLifecycle() {
|
||||
final TestableProcess process = new TestableProcess();
|
||||
final ProcessWatchdog watchdog = new ProcessWatchdog(process, 3);
|
||||
process.setStateMachineTracer(watchdog);
|
||||
process.restart();
|
||||
|
||||
// both process and watchdog are running now. Let's stop the process and see if the watchdog will restart it
|
||||
terminateProcess(process);
|
||||
|
||||
// see if the process is starting again
|
||||
Assertions.assertThat(isInStartingState(process)).isTrue();
|
||||
|
||||
// repeat
|
||||
terminateProcess(process);
|
||||
Assertions.assertThat(isInStartingState(process)).isTrue();
|
||||
|
||||
terminateProcess(process);
|
||||
Assertions.assertThat(isInStartingState(process)).isTrue();
|
||||
|
||||
// this is the 4th termination, we give up trying
|
||||
terminateProcess(process);
|
||||
|
||||
Assertions.assertThat(watchdog.isActive()).isFalse();
|
||||
Assertions.assertThat(process.isInState(ProcessState.TERMINATED)).isTrue();
|
||||
}
|
||||
|
||||
private void terminateProcess(TestableProcess process) {
|
||||
process.onEvent(ProcessEvent.PROCESS_TERMINATED);
|
||||
}
|
||||
|
||||
private boolean isInStartingState(TestableProcess process) {
|
||||
return process.isInState(ProcessState.STARTING);
|
||||
}
|
||||
}
|
||||
@@ -16,8 +16,11 @@
|
||||
*/
|
||||
package org.graylog.datanode.management;
|
||||
|
||||
import com.github.oxo42.stateless4j.StateMachine;
|
||||
import org.graylog.datanode.process.ProcessEvent;
|
||||
import org.graylog.datanode.process.ProcessState;
|
||||
import org.graylog.datanode.process.ProcessStateMachine;
|
||||
import org.graylog.datanode.process.StateMachineTracer;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -25,10 +28,10 @@ public class TestableProcess implements ManagableProcess<String> {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(TestableProcess.class);
|
||||
|
||||
private volatile ProcessState state;
|
||||
private final StateMachine<ProcessState, ProcessEvent> stateMachine;
|
||||
|
||||
public TestableProcess() {
|
||||
this.state = ProcessState.WAITING_FOR_CONFIGURATION;
|
||||
this.stateMachine = ProcessStateMachine.createNew();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -39,22 +42,27 @@ public class TestableProcess implements ManagableProcess<String> {
|
||||
@Override
|
||||
public void restart() {
|
||||
LOG.debug("Starting process");
|
||||
this.state = ProcessState.STARTING;
|
||||
onEvent(ProcessEvent.PROCESS_STARTED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
LOG.debug("Stopping process process");
|
||||
this.state = ProcessState.TERMINATED;
|
||||
onEvent(ProcessEvent.PROCESS_STOPPED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEvent(ProcessEvent event) {
|
||||
// ignored
|
||||
stateMachine.fire(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStateMachineTracer(StateMachineTracer stateMachineTracer) {
|
||||
stateMachine.setTrace(stateMachineTracer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInState(ProcessState state) {
|
||||
return this.state == state;
|
||||
return this.stateMachine.isInState(state);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user