From 36cd75218e43a5649c0fd0474291d8c7d49671c5 Mon Sep 17 00:00:00 2001 From: anhefti Date: Wed, 26 Feb 2020 11:34:31 +0100 Subject: [PATCH] ping indicator performance --- .../session/ClientConnectionTable.java | 3 + .../impl/ClientConnectionDataInternal.java | 26 +++++---- .../impl/DistributedServerPingHandler.java | 3 +- .../session/impl/SingleServerPingHandler.java | 3 +- .../weblayer/api/ExamAPI_V1_Controller.java | 56 +++++++++++++------ .../weblayer/api/IndicatorController.java | 35 +++++++++++- .../api/exam/ExamAPIIntegrationTester.java | 16 ++---- 7 files changed, 97 insertions(+), 45 deletions(-) diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/session/ClientConnectionTable.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/session/ClientConnectionTable.java index 029ebcc9..1806b13d 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/session/ClientConnectionTable.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/session/ClientConnectionTable.java @@ -503,6 +503,9 @@ public final class ClientConnectionTable { final IndicatorValue indicatorValue = this.connectionData.indicatorValues.get(i); final IndicatorData indicatorData = ClientConnectionTable.this.indicatorMapping.get(indicatorValue.getType()); + if (indicatorData == null) { + continue; + } if (!this.connectionData.clientConnection.status.establishedStatus) { final String value = (indicatorData.indicator.type.showOnlyInActiveState) diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ClientConnectionDataInternal.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ClientConnectionDataInternal.java index fa7d222a..791cadf2 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ClientConnectionDataInternal.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ClientConnectionDataInternal.java @@ -14,6 +14,9 @@ import java.util.Collections; import java.util.EnumMap; import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import com.fasterxml.jackson.annotation.JsonProperty; import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection; @@ -23,7 +26,8 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.session.ClientIndicator; public class ClientConnectionDataInternal extends ClientConnectionData { - final Collection pingMappings; + private static final Logger log = LoggerFactory.getLogger(ClientConnectionDataInternal.class); + final EnumMap> indicatorMapping; PingIntervalClientIndicator pingIndicator = null; @@ -35,17 +39,13 @@ public class ClientConnectionDataInternal extends ClientConnectionData { super(clientConnection, clientIndicators); this.indicatorMapping = new EnumMap<>(EventType.class); - this.pingMappings = new ArrayList<>(); for (final ClientIndicator clientIndicator : clientIndicators) { - if (clientIndicator instanceof AbstractPingIndicator) { - if (clientIndicator instanceof PingIntervalClientIndicator) { - this.pingIndicator = (PingIntervalClientIndicator) clientIndicator; - if (!this.pingIndicator.hidden) { - this.pingMappings.add((AbstractPingIndicator) clientIndicator); - } - } else { - this.pingMappings.add((AbstractPingIndicator) clientIndicator); + if (clientIndicator instanceof PingIntervalClientIndicator) { + if (this.pingIndicator != null) { + log.error("Currently only one ping indicator is allowed: {}", clientIndicator); + continue; } + this.pingIndicator = (PingIntervalClientIndicator) clientIndicator; } for (final EventType eventType : clientIndicator.observedEvents()) { this.indicatorMapping @@ -55,6 +55,12 @@ public class ClientConnectionDataInternal extends ClientConnectionData { } } + public final void notifyPing(final long timestamp, final int pingNumber) { + if (this.pingIndicator != null) { + this.pingIndicator.notifyPing(timestamp, pingNumber); + } + } + Collection getIndicatorMapping(final EventType eventType) { if (!this.indicatorMapping.containsKey(eventType)) { return Collections.emptyList(); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/DistributedServerPingHandler.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/DistributedServerPingHandler.java index b1300f1c..7c144e3a 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/DistributedServerPingHandler.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/DistributedServerPingHandler.java @@ -59,8 +59,7 @@ public class DistributedServerPingHandler implements PingHandlingStrategy { this.examSessionCacheService.getActiveClientConnection(connectionToken); if (activeClientConnection != null) { - activeClientConnection.pingMappings - .forEach(pingIndicator -> pingIndicator.notifyPing(timestamp, pingNumber)); + activeClientConnection.notifyPing(timestamp, pingNumber); } } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/SingleServerPingHandler.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/SingleServerPingHandler.java index 4eb62dba..43fd671a 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/SingleServerPingHandler.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/SingleServerPingHandler.java @@ -32,8 +32,7 @@ public class SingleServerPingHandler implements PingHandlingStrategy { this.examSessionCacheService.getActiveClientConnection(connectionToken); if (activeClientConnection != null) { - activeClientConnection.pingMappings - .forEach(pingIndicator -> pingIndicator.notifyPing(timestamp, pingNumber)); + activeClientConnection.notifyPing(timestamp, pingNumber); } } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamAPI_V1_Controller.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamAPI_V1_Controller.java index eaaca281..ab8cd316 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamAPI_V1_Controller.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamAPI_V1_Controller.java @@ -274,43 +274,63 @@ public class ExamAPI_V1_Controller { .ok() .build(); +// @RequestMapping( +// path = API.EXAM_API_PING_ENDPOINT, +// method = RequestMethod.POST, +// consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE, +// produces = MediaType.APPLICATION_JSON_UTF8_VALUE) +// public CompletableFuture> ping( +// @RequestHeader(name = API.EXAM_API_SEB_CONNECTION_TOKEN, required = true) final String connectionToken, +// @RequestParam(name = API.EXAM_API_PING_TIMESTAMP, required = true) final long timestamp, +// @RequestParam(name = API.EXAM_API_PING_NUMBER, required = false) final int pingNumber) { +// +// return CompletableFuture.supplyAsync( +// () -> { +// final String notifyPing = this.sebClientConnectionService +// .notifyPing(connectionToken, timestamp, pingNumber); +// if (notifyPing == null) { +// return EMPTY_PING_RESPONSE; +// } +// +// return ResponseEntity +// .ok() +// .body(notifyPing); +// }, +// this.executor); +// } + @RequestMapping( path = API.EXAM_API_PING_ENDPOINT, method = RequestMethod.POST, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) - public CompletableFuture> ping( + public ResponseEntity ping( @RequestHeader(name = API.EXAM_API_SEB_CONNECTION_TOKEN, required = true) final String connectionToken, @RequestParam(name = API.EXAM_API_PING_TIMESTAMP, required = true) final long timestamp, @RequestParam(name = API.EXAM_API_PING_NUMBER, required = false) final int pingNumber) { - return CompletableFuture.supplyAsync( - () -> { - final String notifyPing = this.sebClientConnectionService - .notifyPing(connectionToken, timestamp, pingNumber); - if (notifyPing == null) { - return EMPTY_PING_RESPONSE; - } + final String instruction = this.sebClientConnectionService + .notifyPing(connectionToken, timestamp, pingNumber); - return ResponseEntity - .ok() - .body(notifyPing); - }, - this.executor); + if (instruction == null) { + return EMPTY_PING_RESPONSE; + } + + return ResponseEntity + .ok() + .body(instruction); } @RequestMapping( path = API.EXAM_API_EVENT_ENDPOINT, method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_UTF8_VALUE) - public CompletableFuture event( + public void event( @RequestHeader(name = API.EXAM_API_SEB_CONNECTION_TOKEN, required = true) final String connectionToken, @RequestBody(required = true) final ClientEvent event) { - return CompletableFuture.runAsync( - () -> this.sebClientConnectionService - .notifyClientEvent(connectionToken, event), - this.executor); + this.sebClientConnectionService + .notifyClientEvent(connectionToken, event); } private Long getInstitutionId(final Principal principal) { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/IndicatorController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/IndicatorController.java index 117b6699..2f3a28f3 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/IndicatorController.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/IndicatorController.java @@ -16,9 +16,11 @@ import ch.ethz.seb.sebserver.gbl.api.API; import ch.ethz.seb.sebserver.gbl.api.EntityType; import ch.ethz.seb.sebserver.gbl.api.POSTMapper; import ch.ethz.seb.sebserver.gbl.model.Domain; +import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport; import ch.ethz.seb.sebserver.gbl.model.GrantEntity; import ch.ethz.seb.sebserver.gbl.model.exam.Indicator; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; +import ch.ethz.seb.sebserver.gbl.util.Pair; import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.IndicatorRecordDynamicSqlSupport; import ch.ethz.seb.sebserver.webservice.servicelayer.PaginationService; @@ -27,6 +29,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkActionServic import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.IndicatorDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO; +import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamSessionService; import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationService; @WebServiceProfile @@ -35,6 +38,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationSe public class IndicatorController extends EntityController { private final ExamDAO examDao; + private final ExamSessionService examSessionService; protected IndicatorController( final AuthorizationService authorization, @@ -43,7 +47,8 @@ public class IndicatorController extends EntityController final ExamDAO examDao, final UserActivityLogDAO userActivityLogDAO, final PaginationService paginationService, - final BeanValidationService beanValidationService) { + final BeanValidationService beanValidationService, + final ExamSessionService examSessionService) { super(authorization, bulkActionService, @@ -53,6 +58,7 @@ public class IndicatorController extends EntityController beanValidationService); this.examDao = examDao; + this.examSessionService = examSessionService; } @Override @@ -95,4 +101,31 @@ public class IndicatorController extends EntityController return EntityType.EXAM; } + @Override + protected Result notifyCreated(final Indicator entity) { + flushExamSessionCaches(entity); + return super.notifyCreated(entity); + } + + @Override + protected Result notifySaved(final Indicator entity) { + flushExamSessionCaches(entity); + return super.notifySaved(entity); + } + + @Override + protected Result> notifyDeleted( + final Pair pair) { + + flushExamSessionCaches(pair.a); + return super.notifyDeleted(pair); + } + + private void flushExamSessionCaches(final Indicator entity) { + if (this.examSessionService.isExamRunning(entity.examId)) { + this.examSessionService.flushCache(this.examSessionService.getRunningExam(entity.examId).getOrThrow()); + } + + } + } diff --git a/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/exam/ExamAPIIntegrationTester.java b/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/exam/ExamAPIIntegrationTester.java index 3e253f74..ce69f36a 100644 --- a/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/exam/ExamAPIIntegrationTester.java +++ b/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/exam/ExamAPIIntegrationTester.java @@ -259,12 +259,8 @@ public abstract class ExamAPIIntegrationTester { + "&" + API.EXAM_API_PING_NUMBER + "=" + num; builder.content(body); - final MvcResult mvcResult = this.mockMvc - .perform(builder) - .andExpect(request().asyncStarted()) - .andDo(MockMvcResultHandlers.log()) - .andReturn(); - final ResultActions result = this.mockMvc.perform(asyncDispatch(mvcResult)); + final ResultActions result = this.mockMvc + .perform(builder); return result.andReturn().getResponse(); } @@ -284,12 +280,8 @@ public abstract class ExamAPIIntegrationTester { final String body = "{ \"type\": \"%s\", \"timestamp\": %s, \"numericValue\": %s, \"text\": \"%s\" }"; builder.content(String.format(body, type, timestamp, value, text)); - final MvcResult mvcResult = this.mockMvc - .perform(builder) - .andExpect(request().asyncStarted()) - .andDo(MockMvcResultHandlers.log()) - .andReturn(); - final ResultActions result = this.mockMvc.perform(asyncDispatch(mvcResult)); + final ResultActions result = this.mockMvc + .perform(builder); return result.andReturn().getResponse(); }