ping indicator performance

This commit is contained in:
anhefti 2020-02-26 11:34:31 +01:00
parent 43578d3e1c
commit 36cd75218e
7 changed files with 97 additions and 45 deletions

View file

@ -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)

View file

@ -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<AbstractPingIndicator> pingMappings;
private static final Logger log = LoggerFactory.getLogger(ClientConnectionDataInternal.class);
final EnumMap<EventType, Collection<ClientIndicator>> 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) {
if (this.pingIndicator != null) {
log.error("Currently only one ping indicator is allowed: {}", clientIndicator);
continue;
}
this.pingIndicator = (PingIntervalClientIndicator) clientIndicator;
if (!this.pingIndicator.hidden) {
this.pingMappings.add((AbstractPingIndicator) clientIndicator);
}
} else {
this.pingMappings.add((AbstractPingIndicator) 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<ClientIndicator> getIndicatorMapping(final EventType eventType) {
if (!this.indicatorMapping.containsKey(eventType)) {
return Collections.emptyList();

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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<ResponseEntity<String>> 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<ResponseEntity<String>> ping(
public ResponseEntity<String> 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
final String instruction = this.sebClientConnectionService
.notifyPing(connectionToken, timestamp, pingNumber);
if (notifyPing == null) {
if (instruction == null) {
return EMPTY_PING_RESPONSE;
}
return ResponseEntity
.ok()
.body(notifyPing);
},
this.executor);
.body(instruction);
}
@RequestMapping(
path = API.EXAM_API_EVENT_ENDPOINT,
method = RequestMethod.POST,
consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
public CompletableFuture<Void> 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) {

View file

@ -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<Indicator, Indicator> {
private final ExamDAO examDao;
private final ExamSessionService examSessionService;
protected IndicatorController(
final AuthorizationService authorization,
@ -43,7 +47,8 @@ public class IndicatorController extends EntityController<Indicator, Indicator>
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<Indicator, Indicator>
beanValidationService);
this.examDao = examDao;
this.examSessionService = examSessionService;
}
@Override
@ -95,4 +101,31 @@ public class IndicatorController extends EntityController<Indicator, Indicator>
return EntityType.EXAM;
}
@Override
protected Result<Indicator> notifyCreated(final Indicator entity) {
flushExamSessionCaches(entity);
return super.notifyCreated(entity);
}
@Override
protected Result<Indicator> notifySaved(final Indicator entity) {
flushExamSessionCaches(entity);
return super.notifySaved(entity);
}
@Override
protected Result<Pair<Indicator, EntityProcessingReport>> notifyDeleted(
final Pair<Indicator, EntityProcessingReport> 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());
}
}
}

View file

@ -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();
}