SEBSERV-195 improved ping indicator for distributed setups

This commit is contained in:
anhefti 2021-07-15 14:50:06 +02:00
parent 494c6a08ec
commit 3296b7c004
6 changed files with 16 additions and 35 deletions

View file

@ -15,7 +15,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.session.ClientIndicator;
public abstract class AbstractClientIndicator implements ClientIndicator { public abstract class AbstractClientIndicator implements ClientIndicator {
private static final long PERSISTENT_UPDATE_INTERVAL = 1 * Constants.SECOND_IN_MILLIS; private static final long PERSISTENT_UPDATE_INTERVAL = Constants.SECOND_IN_MILLIS;
protected Long indicatorId; protected Long indicatorId;
protected Long examId; protected Long examId;

View file

@ -15,26 +15,21 @@ import java.util.Set;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import org.joda.time.DateTimeZone; import org.joda.time.DateTimeZone;
import com.fasterxml.jackson.annotation.JsonIgnore; import ch.ethz.seb.sebserver.gbl.Constants;
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.session.ClientEvent.EventType; import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent.EventType;
import ch.ethz.seb.sebserver.gbl.util.Utils;
import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.ClientEventRecord; import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.ClientEventRecord;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ClientEventDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ClientEventDAO;
public abstract class AbstractPingIndicator extends AbstractClientIndicator { public abstract class AbstractPingIndicator extends AbstractClientIndicator {
private static final int PING_COUNT_INTERVAL_FOR_PERSISTENT_UPDATE = 2; private static final long INTERVAL_FOR_PERSISTENT_UPDATE = Constants.SECOND_IN_MILLIS;
private final Set<EventType> EMPTY_SET = Collections.unmodifiableSet(EnumSet.noneOf(EventType.class)); private final Set<EventType> EMPTY_SET = Collections.unmodifiableSet(EnumSet.noneOf(EventType.class));
protected final ClientEventDAO clientEventDAO; protected final ClientEventDAO clientEventDAO;
protected long pingLatency; private long lastUpdate = 0;
protected int pingCount = 0;
protected int pingNumber = 0;
protected ClientEventRecord pingRecord = null; protected ClientEventRecord pingRecord = null;
protected AbstractPingIndicator(final ClientEventDAO clientEventDAO) { protected AbstractPingIndicator(final ClientEventDAO clientEventDAO) {
@ -60,21 +55,19 @@ public abstract class AbstractPingIndicator extends AbstractClientIndicator {
public final void notifyPing(final long timestamp, final int pingNumber) { public final void notifyPing(final long timestamp, final int pingNumber) {
final long now = DateTime.now(DateTimeZone.UTC).getMillis(); final long now = DateTime.now(DateTimeZone.UTC).getMillis();
this.pingLatency = now - timestamp;
super.currentValue = now; super.currentValue = now;
this.pingCount++;
this.pingNumber = pingNumber;
super.lastPersistentUpdate = now; super.lastPersistentUpdate = now;
if (!this.cachingEnabled && if (!this.cachingEnabled && this.pingRecord != null) {
this.pingCount > PING_COUNT_INTERVAL_FOR_PERSISTENT_UPDATE &&
this.pingRecord != null) {
// Update last ping time on persistent storage // Update last ping time on persistent storage
this.pingRecord.setClientTime(timestamp); final long millisecondsNow = System.currentTimeMillis();
this.pingRecord.setServerTime(Utils.getMillisecondsNow()); if (millisecondsNow - this.lastUpdate > INTERVAL_FOR_PERSISTENT_UPDATE) {
this.clientEventDAO.updatePingEvent(this.pingRecord); this.pingRecord.setClientTime(timestamp);
this.pingCount = 0; this.pingRecord.setServerTime(millisecondsNow);
this.clientEventDAO.updatePingEvent(this.pingRecord);
this.lastUpdate = millisecondsNow;
}
} }
} }
@ -83,11 +76,6 @@ public abstract class AbstractPingIndicator extends AbstractClientIndicator {
return this.EMPTY_SET; return this.EMPTY_SET;
} }
@JsonIgnore
public int getPingNumber() {
return this.pingNumber;
}
public abstract ClientEventRecord updateLogEvent(final long now); public abstract ClientEventRecord updateLogEvent(final long now);
} }

View file

@ -1,11 +1,11 @@
server.address=localhost server.address=localhost
server.port=8080 server.port=8090
sebserver.gui.http.external.scheme=http sebserver.gui.http.external.scheme=http
sebserver.gui.entrypoint=/gui sebserver.gui.entrypoint=/gui
sebserver.gui.webservice.protocol=http sebserver.gui.webservice.protocol=http
sebserver.gui.webservice.address=localhost sebserver.gui.webservice.address=localhost
sebserver.gui.webservice.port=8080 sebserver.gui.webservice.port=8090
sebserver.gui.webservice.apipath=/admin-api/v1 sebserver.gui.webservice.apipath=/admin-api/v1
# defines the polling interval that is used to poll the webservice for client connection data on a monitored exam page # defines the polling interval that is used to poll the webservice for client connection data on a monitored exam page
sebserver.gui.webservice.poll-interval=1000 sebserver.gui.webservice.poll-interval=1000

View file

@ -22,7 +22,7 @@ sebserver.webservice.clean-db-on-startup=false
# webservice configuration # webservice configuration
sebserver.init.adminaccount.gen-on-init=false sebserver.init.adminaccount.gen-on-init=false
sebserver.webservice.distributed=false sebserver.webservice.distributed=true
sebserver.webservice.master.delay.threshold=10000 sebserver.webservice.master.delay.threshold=10000
sebserver.webservice.http.external.scheme=http sebserver.webservice.http.external.scheme=http
sebserver.webservice.http.external.servername=localhost sebserver.webservice.http.external.servername=localhost

View file

@ -380,7 +380,7 @@ sebserver.quizdiscovery.action.import=Import as Exam
sebserver.quizdiscovery.quiz.import.out.dated=The Selected LMS exam is already finished and can't be imported sebserver.quizdiscovery.quiz.import.out.dated=The Selected LMS exam is already finished and can't be imported
sebserver.quizdiscovery.action.details=Show LMS Exam Details sebserver.quizdiscovery.action.details=Show LMS Exam Details
sebserver.quizdiscovery.quiz.import.existing.confirm=This course was already imported and importing it twice may lead to<br/> unexpected behavior within automated SEB restriction on LMS.<br/><br/> Do you want to import this course as exam anyway? sebserver.quizdiscovery.quiz.import.existing.confirm=This course was already imported and importing it twice may lead to<br/> unexpected behavior within automated SEB restriction on LMS.<br/><br/> Do you want to import this course as exam anyway?
sebserver.quizdiscovery.quiz.import.existing=This course was already imported as an exam.<br/> You will find it in the Exam section. sebserver.quizdiscovery.quiz.import.existing=This course was already imported as an exam.<br/>Please find it in the Exam section.
sebserver.quizdiscovery.quiz.details.title=LMS Exam Details sebserver.quizdiscovery.quiz.details.title=LMS Exam Details
sebserver.quizdiscovery.quiz.details.institution=Institution sebserver.quizdiscovery.quiz.details.institution=Institution

View file

@ -38,7 +38,6 @@ import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.ClientConnectionRe
import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.ClientEventRecord; import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.ClientEventRecord;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.ClientConnectionDataInternal; import ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.ClientConnectionDataInternal;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.ExamSessionCacheService; import ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.ExamSessionCacheService;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.indicator.AbstractPingIndicator;
@Sql(scripts = { "classpath:schema-test.sql", "classpath:data-test.sql", "classpath:data-test-additional.sql" }) @Sql(scripts = { "classpath:schema-test.sql", "classpath:data-test.sql", "classpath:data-test-additional.sql" })
public class SebConnectionTest extends ExamAPIIntegrationTester { public class SebConnectionTest extends ExamAPIIntegrationTester {
@ -468,20 +467,15 @@ public class SebConnectionTest extends ExamAPIIntegrationTester {
assertFalse(ccdi.indicatorValues.isEmpty()); assertFalse(ccdi.indicatorValues.isEmpty());
final IndicatorValue pingIndicator = ccdi.indicatorValues.iterator().next(); final IndicatorValue pingIndicator = ccdi.indicatorValues.iterator().next();
assertTrue(pingIndicator.getType() == IndicatorType.LAST_PING); assertTrue(pingIndicator.getType() == IndicatorType.LAST_PING);
assertEquals("0", String.valueOf(((AbstractPingIndicator) pingIndicator).getPingNumber()));
super.sendPing(accessToken, connectionToken, 1); super.sendPing(accessToken, connectionToken, 1);
Thread.sleep(200); Thread.sleep(200);
assertEquals("1", String.valueOf(((AbstractPingIndicator) pingIndicator).getPingNumber()));
super.sendPing(accessToken, connectionToken, 2); super.sendPing(accessToken, connectionToken, 2);
Thread.sleep(200); Thread.sleep(200);
assertEquals("2", String.valueOf(((AbstractPingIndicator) pingIndicator).getPingNumber()));
super.sendPing(accessToken, connectionToken, 3); super.sendPing(accessToken, connectionToken, 3);
Thread.sleep(200); Thread.sleep(200);
assertEquals("3", String.valueOf(((AbstractPingIndicator) pingIndicator).getPingNumber()));
super.sendPing(accessToken, connectionToken, 5); super.sendPing(accessToken, connectionToken, 5);
Thread.sleep(200); Thread.sleep(200);
assertEquals("5", String.valueOf(((AbstractPingIndicator) pingIndicator).getPingNumber()));
} }
@Test @Test
@ -531,7 +525,6 @@ public class SebConnectionTest extends ExamAPIIntegrationTester {
assertFalse(ccdi.indicatorValues.isEmpty()); assertFalse(ccdi.indicatorValues.isEmpty());
final IndicatorValue pingIndicator = ccdi.indicatorValues.iterator().next(); final IndicatorValue pingIndicator = ccdi.indicatorValues.iterator().next();
assertTrue(pingIndicator.getType() == IndicatorType.LAST_PING); assertTrue(pingIndicator.getType() == IndicatorType.LAST_PING);
assertEquals("0", String.valueOf(((AbstractPingIndicator) pingIndicator).getPingNumber()));
MockHttpServletResponse sendEvent = super.sendEvent( MockHttpServletResponse sendEvent = super.sendEvent(
accessToken, accessToken,