SEBSERV-250,SEBSERV-188

- implemented overall generic incident marker for client connection
handler and removed specific missingPing incident.
- adapted missing ping handling to new overall generic incident marker
This commit is contained in:
anhefti 2022-01-13 13:07:02 +01:00
parent bad7510a63
commit 50aef06db0
12 changed files with 103 additions and 78 deletions

View file

@ -73,6 +73,11 @@ public class ClientConnectionData {
return this.clientConnection.id; return this.clientConnection.id;
} }
@JsonIgnore
public boolean hasAnyIncident() {
return this.missingPing || this.pendingNotification;
}
public ClientConnection getClientConnection() { public ClientConnection getClientConnection() {
return this.clientConnection; return this.clientConnection;
} }

View file

@ -67,6 +67,16 @@ public interface ClientIndicator extends IndicatorValue {
* @param event The ClientEvent instance */ * @param event The ClientEvent instance */
void notifyValueChange(ClientEvent event); void notifyValueChange(ClientEvent event);
/** This gets called on a value change e.g.: when a ClientEvent was received.
* NOTE: that this is called only on the same machine (server-instance) on that the ClientEvent was received.
*
* @param clientEventRecord The ClientEventRecord instance */
void notifyValueChange(ClientEventRecord clientEventRecord); void notifyValueChange(ClientEventRecord clientEventRecord);
/** This indicates if the indicator indicates an incident. This is the case if the actual indicator value
* is above or below the max or min value defined by the indicator threshold settings.
*
* @return true if this indicator indicates an incident */
boolean hasIncident();
} }

View file

@ -17,6 +17,7 @@ import java.util.List;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection; import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection;
@ -74,14 +75,29 @@ public class ClientConnectionDataInternal extends ClientConnectionData {
@Override @Override
@JsonProperty(ATTR_MISSING_PING) @JsonProperty(ATTR_MISSING_PING)
public Boolean getMissingPing() { public final Boolean getMissingPing() {
return this.pingIndicator != null && this.pingIndicator.isMissingPing(); return this.pingIndicator != null && this.pingIndicator.hasIncident();
} }
@Override @Override
@JsonProperty(ATTR_PENDING_NOTIFICATION) @JsonProperty(ATTR_PENDING_NOTIFICATION)
public Boolean pendingNotification() { public final Boolean pendingNotification() {
return this.pendingNotificationIndication.notifictionPending(); return this.pendingNotificationIndication.notifictionPending();
} }
@Override
@JsonIgnore
public final boolean hasAnyIncident() {
return pendingNotification() || hasIncident();
}
private boolean hasIncident() {
return this.indicatorMapping.values()
.stream()
.flatMap(Collection::stream)
.filter(ClientIndicator::hasIncident)
.findFirst()
.isPresent();
}
} }

View file

@ -847,7 +847,7 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic
return connection -> { return connection -> {
if (connection.pingIndicator.missingPingUpdate(now)) { if (connection.pingIndicator.missingPingUpdate(now)) {
final boolean missingPing = connection.pingIndicator.isMissingPing(); final boolean missingPing = connection.getMissingPing();
final ClientEventRecord clientEventRecord = new ClientEventRecord( final ClientEventRecord clientEventRecord = new ClientEventRecord(
null, null,
connection.getConnectionId(), connection.getConnectionId(),

View file

@ -8,6 +8,8 @@
package ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.indicator; package ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.indicator;
import java.util.Comparator;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -21,8 +23,8 @@ public abstract class AbstractClientIndicator implements ClientIndicator {
protected final DistributedIndicatorValueService distributedPingCache; protected final DistributedIndicatorValueService distributedPingCache;
protected Long indicatorId; protected Long indicatorId = -1L;
protected Long examId; protected Long examId = -1L;
protected Long connectionId; protected Long connectionId;
protected boolean cachingEnabled; protected boolean cachingEnabled;
protected boolean active = true; protected boolean active = true;
@ -32,6 +34,8 @@ public abstract class AbstractClientIndicator implements ClientIndicator {
protected boolean initialized = false; protected boolean initialized = false;
protected double currentValue = Double.NaN; protected double currentValue = Double.NaN;
protected double incidentThreshold = 0.0;
public AbstractClientIndicator(final DistributedIndicatorValueService distributedPingCache) { public AbstractClientIndicator(final DistributedIndicatorValueService distributedPingCache) {
super(); super();
this.distributedPingCache = distributedPingCache; this.distributedPingCache = distributedPingCache;
@ -44,12 +48,20 @@ public abstract class AbstractClientIndicator implements ClientIndicator {
final boolean active, final boolean active,
final boolean cachingEnabled) { final boolean cachingEnabled) {
this.indicatorId = (indicatorDefinition != null && indicatorDefinition.id != null) if (indicatorDefinition != null) {
? indicatorDefinition.id this.incidentThreshold = (!indicatorDefinition.type.inverse)
: -1; ? indicatorDefinition.thresholds.stream()
this.examId = (indicatorDefinition != null && indicatorDefinition.examId != null) .map(t -> t.value)
? indicatorDefinition.examId .max(Comparator.naturalOrder())
: -1; .orElse(0.0)
: indicatorDefinition.thresholds.stream()
.map(t -> t.value)
.min(Comparator.naturalOrder())
.orElse(0.0);
this.indicatorId = indicatorDefinition.id;
this.examId = indicatorDefinition.examId;
}
this.connectionId = connectionId; this.connectionId = connectionId;
this.active = active; this.active = active;
this.cachingEnabled = cachingEnabled; this.cachingEnabled = cachingEnabled;

View file

@ -47,13 +47,9 @@ public abstract class AbstractLogLevelCountIndicator extends AbstractLogIndicato
valueChanged(clientEventRecord.getText()); valueChanged(clientEventRecord.getText());
} }
private void valueChanged(final String eventText) { @Override
if (this.tags == null || this.tags.length == 0 || hasTag(eventText)) { public final boolean hasIncident() {
if (super.ditributedIndicatorValueRecordId != null) { return this.currentValue > this.incidentThreshold;
this.distributedPingCache.incrementIndicatorValue(super.ditributedIndicatorValueRecordId);
}
this.currentValue = getValue() + 1d;
}
} }
@Override @Override
@ -114,4 +110,13 @@ public abstract class AbstractLogLevelCountIndicator extends AbstractLogIndicato
return result; return result;
} }
private void valueChanged(final String eventText) {
if (this.tags == null || this.tags.length == 0 || hasTag(eventText)) {
if (super.ditributedIndicatorValueRecordId != null) {
this.distributedPingCache.incrementIndicatorValue(super.ditributedIndicatorValueRecordId);
}
this.currentValue = getValue() + 1d;
}
}
} }

View file

@ -33,6 +33,11 @@ public abstract class AbstractPingIndicator extends AbstractClientIndicator {
super.init(indicatorDefinition, connectionId, active, cachingEnabled); super.init(indicatorDefinition, connectionId, active, cachingEnabled);
} }
@Override
public Set<EventType> observedEvents() {
return this.EMPTY_SET;
}
public final void notifyPing(final long timestamp, final int pingNumber) { public final void notifyPing(final long timestamp, final int pingNumber) {
super.currentValue = timestamp; super.currentValue = timestamp;
@ -49,11 +54,4 @@ public abstract class AbstractPingIndicator extends AbstractClientIndicator {
} }
} }
@Override
public Set<EventType> observedEvents() {
return this.EMPTY_SET;
}
public abstract boolean missingPingUpdate(final long now);
} }

View file

@ -48,4 +48,9 @@ public class BatteryStatusIndicator extends AbstractLogNumberIndicator {
return IndicatorType.BATTERY_STATUS; return IndicatorType.BATTERY_STATUS;
} }
@Override
public final boolean hasIncident() {
return this.currentValue < this.incidentThreshold;
}
} }

View file

@ -8,11 +8,7 @@
package ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.indicator; package ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.indicator;
import java.util.Comparator;
import org.joda.time.DateTimeUtils; import org.joda.time.DateTimeUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.Scope;
@ -32,15 +28,11 @@ import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.ClientEventRecord;
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public final class PingIntervalClientIndicator extends AbstractPingIndicator { public final class PingIntervalClientIndicator extends AbstractPingIndicator {
private static final Logger log = LoggerFactory.getLogger(PingIntervalClientIndicator.class);
// This is the default ping error threshold that is set if the threshold cannot be get // This is the default ping error threshold that is set if the threshold cannot be get
// from the ping threshold settings. If the last ping is older then this interval back in time // from the ping threshold settings. If the last ping is older then this interval back in time
// then the ping is considered and marked as missing // then the ping is considered and marked as missing
private static final long DEFAULT_PING_ERROR_THRESHOLD = Constants.SECOND_IN_MILLIS * 5; private static final long DEFAULT_PING_ERROR_THRESHOLD = Constants.SECOND_IN_MILLIS * 5;
private long pingErrorThreshold;
private boolean missingPing = false;
private boolean hidden = false; private boolean hidden = false;
public PingIntervalClientIndicator(final DistributedIndicatorValueService distributedPingCache) { public PingIntervalClientIndicator(final DistributedIndicatorValueService distributedPingCache) {
@ -62,35 +54,11 @@ public final class PingIntervalClientIndicator extends AbstractPingIndicator {
super.init(indicatorDefinition, connectionId, active, cachingEnabled); super.init(indicatorDefinition, connectionId, active, cachingEnabled);
// init ping error threshold this.lastCheckVal = getValue();
try {
indicatorDefinition if (this.incidentThreshold <= 0.0) {
.getThresholds() this.incidentThreshold = DEFAULT_PING_ERROR_THRESHOLD;
.stream()
.max(Comparator.naturalOrder())
.ifPresent(t -> this.pingErrorThreshold = t.value.longValue());
} catch (final Exception e) {
log.error("Failed to initialize pingErrorThreshold: {}", e.getMessage());
this.pingErrorThreshold = DEFAULT_PING_ERROR_THRESHOLD;
} }
// init missing ping indicator
if (!cachingEnabled) {
try {
this.missingPing = this.pingErrorThreshold < getValue();
} catch (final Exception e) {
log.error("Failed to initialize missingPing: {}", e.getMessage());
this.missingPing = true;
}
}
}
@JsonIgnore
public final boolean isMissingPing() {
return this.missingPing;
} }
@JsonIgnore @JsonIgnore
@ -146,25 +114,23 @@ public final class PingIntervalClientIndicator extends AbstractPingIndicator {
} }
@Override @Override
public boolean missingPingUpdate(final long now) { public final boolean hasIncident() {
return getValue() > super.incidentThreshold;
}
private double lastCheckVal = 0;
public final boolean missingPingUpdate(final long now) {
if (this.currentValue <= 0) { if (this.currentValue <= 0) {
return false; return false;
} }
final long value = now - (long) super.currentValue; final double val = now - this.currentValue;
if (this.missingPing) { // check if incidentThreshold was passed (up or down) since last update
if (this.pingErrorThreshold > value) { final boolean result = (this.lastCheckVal < this.incidentThreshold && val >= this.incidentThreshold) ||
this.missingPing = false; (this.lastCheckVal >= this.incidentThreshold && val < this.incidentThreshold);
return true; this.lastCheckVal = val;
} return result;
} else {
if (this.pingErrorThreshold < value) {
this.missingPing = true;
return true;
}
}
return false;
} }
} }

View file

@ -48,4 +48,9 @@ public class WLANStatusIndicator extends AbstractLogNumberIndicator {
return IndicatorType.WLAN_STATUS; return IndicatorType.WLAN_STATUS;
} }
@Override
public final boolean hasIncident() {
return this.currentValue < this.incidentThreshold;
}
} }

View file

@ -17,6 +17,7 @@ import com.fasterxml.jackson.core.JsonProcessingException;
import ch.ethz.seb.sebserver.gbl.api.JSONMapper; import ch.ethz.seb.sebserver.gbl.api.JSONMapper;
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator; import ch.ethz.seb.sebserver.gbl.model.exam.Indicator;
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator.IndicatorType;
public class IndicatorValueJSONTest { public class IndicatorValueJSONTest {
@ -25,7 +26,7 @@ public class IndicatorValueJSONTest {
final JSONMapper jsonMapper = new JSONMapper(); final JSONMapper jsonMapper = new JSONMapper();
final DistributedIndicatorValueService mock = Mockito.mock(DistributedIndicatorValueService.class); final DistributedIndicatorValueService mock = Mockito.mock(DistributedIndicatorValueService.class);
final ErrorLogCountClientIndicator indicator = new ErrorLogCountClientIndicator(mock, null); final ErrorLogCountClientIndicator indicator = new ErrorLogCountClientIndicator(mock, null);
indicator.init(new Indicator(1L, null, null, null, null, null, null, null), 2L, true, true); indicator.init(new Indicator(1L, 2L, "test", IndicatorType.NONE, null, null, null, null), 2L, true, true);
final String json = jsonMapper.writeValueAsString(indicator); final String json = jsonMapper.writeValueAsString(indicator);
assertEquals("{\"id\":1,\"val\":\"NaN\"}", json); assertEquals("{\"id\":1,\"val\":\"NaN\"}", json);
} }

View file

@ -19,6 +19,7 @@ import com.fasterxml.jackson.core.JsonProcessingException;
import ch.ethz.seb.sebserver.gbl.api.JSONMapper; import ch.ethz.seb.sebserver.gbl.api.JSONMapper;
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator; import ch.ethz.seb.sebserver.gbl.model.exam.Indicator;
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator.IndicatorType;
public class PingIntervalClientIndicatorTest { public class PingIntervalClientIndicatorTest {
@ -68,7 +69,8 @@ public class PingIntervalClientIndicatorTest {
final PingIntervalClientIndicator pingIntervalClientIndicator = final PingIntervalClientIndicator pingIntervalClientIndicator =
new PingIntervalClientIndicator(distributedPingCache); new PingIntervalClientIndicator(distributedPingCache);
pingIntervalClientIndicator.init(new Indicator(2L, null, null, null, null, null, null, null), 1L, true, true); pingIntervalClientIndicator.init(new Indicator(2L, 3L, "test", IndicatorType.NONE, null, null, null, null), 1L,
true, true);
final JSONMapper jsonMapper = new JSONMapper(); final JSONMapper jsonMapper = new JSONMapper();
final String json = jsonMapper.writeValueAsString(pingIntervalClientIndicator); final String json = jsonMapper.writeValueAsString(pingIntervalClientIndicator);
assertEquals("{\"id\":2,\"val\":0.0}", json); assertEquals("{\"id\":2,\"val\":0.0}", json);