ping indicator performance
This commit is contained in:
parent
43578d3e1c
commit
36cd75218e
7 changed files with 97 additions and 45 deletions
|
@ -503,6 +503,9 @@ public final class ClientConnectionTable {
|
||||||
final IndicatorValue indicatorValue = this.connectionData.indicatorValues.get(i);
|
final IndicatorValue indicatorValue = this.connectionData.indicatorValues.get(i);
|
||||||
final IndicatorData indicatorData =
|
final IndicatorData indicatorData =
|
||||||
ClientConnectionTable.this.indicatorMapping.get(indicatorValue.getType());
|
ClientConnectionTable.this.indicatorMapping.get(indicatorValue.getType());
|
||||||
|
if (indicatorData == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (!this.connectionData.clientConnection.status.establishedStatus) {
|
if (!this.connectionData.clientConnection.status.establishedStatus) {
|
||||||
final String value = (indicatorData.indicator.type.showOnlyInActiveState)
|
final String value = (indicatorData.indicator.type.showOnlyInActiveState)
|
||||||
|
|
|
@ -14,6 +14,9 @@ import java.util.Collections;
|
||||||
import java.util.EnumMap;
|
import java.util.EnumMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
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;
|
||||||
|
@ -23,7 +26,8 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.session.ClientIndicator;
|
||||||
|
|
||||||
public class ClientConnectionDataInternal extends ClientConnectionData {
|
public class ClientConnectionDataInternal extends ClientConnectionData {
|
||||||
|
|
||||||
final Collection<AbstractPingIndicator> pingMappings;
|
private static final Logger log = LoggerFactory.getLogger(ClientConnectionDataInternal.class);
|
||||||
|
|
||||||
final EnumMap<EventType, Collection<ClientIndicator>> indicatorMapping;
|
final EnumMap<EventType, Collection<ClientIndicator>> indicatorMapping;
|
||||||
|
|
||||||
PingIntervalClientIndicator pingIndicator = null;
|
PingIntervalClientIndicator pingIndicator = null;
|
||||||
|
@ -35,17 +39,13 @@ public class ClientConnectionDataInternal extends ClientConnectionData {
|
||||||
super(clientConnection, clientIndicators);
|
super(clientConnection, clientIndicators);
|
||||||
|
|
||||||
this.indicatorMapping = new EnumMap<>(EventType.class);
|
this.indicatorMapping = new EnumMap<>(EventType.class);
|
||||||
this.pingMappings = new ArrayList<>();
|
|
||||||
for (final ClientIndicator clientIndicator : clientIndicators) {
|
for (final ClientIndicator clientIndicator : clientIndicators) {
|
||||||
if (clientIndicator instanceof AbstractPingIndicator) {
|
if (clientIndicator instanceof PingIntervalClientIndicator) {
|
||||||
if (clientIndicator instanceof PingIntervalClientIndicator) {
|
if (this.pingIndicator != null) {
|
||||||
this.pingIndicator = (PingIntervalClientIndicator) clientIndicator;
|
log.error("Currently only one ping indicator is allowed: {}", clientIndicator);
|
||||||
if (!this.pingIndicator.hidden) {
|
continue;
|
||||||
this.pingMappings.add((AbstractPingIndicator) clientIndicator);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.pingMappings.add((AbstractPingIndicator) clientIndicator);
|
|
||||||
}
|
}
|
||||||
|
this.pingIndicator = (PingIntervalClientIndicator) clientIndicator;
|
||||||
}
|
}
|
||||||
for (final EventType eventType : clientIndicator.observedEvents()) {
|
for (final EventType eventType : clientIndicator.observedEvents()) {
|
||||||
this.indicatorMapping
|
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) {
|
Collection<ClientIndicator> getIndicatorMapping(final EventType eventType) {
|
||||||
if (!this.indicatorMapping.containsKey(eventType)) {
|
if (!this.indicatorMapping.containsKey(eventType)) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
|
|
|
@ -59,8 +59,7 @@ public class DistributedServerPingHandler implements PingHandlingStrategy {
|
||||||
this.examSessionCacheService.getActiveClientConnection(connectionToken);
|
this.examSessionCacheService.getActiveClientConnection(connectionToken);
|
||||||
|
|
||||||
if (activeClientConnection != null) {
|
if (activeClientConnection != null) {
|
||||||
activeClientConnection.pingMappings
|
activeClientConnection.notifyPing(timestamp, pingNumber);
|
||||||
.forEach(pingIndicator -> pingIndicator.notifyPing(timestamp, pingNumber));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,8 +32,7 @@ public class SingleServerPingHandler implements PingHandlingStrategy {
|
||||||
this.examSessionCacheService.getActiveClientConnection(connectionToken);
|
this.examSessionCacheService.getActiveClientConnection(connectionToken);
|
||||||
|
|
||||||
if (activeClientConnection != null) {
|
if (activeClientConnection != null) {
|
||||||
activeClientConnection.pingMappings
|
activeClientConnection.notifyPing(timestamp, pingNumber);
|
||||||
.forEach(pingIndicator -> pingIndicator.notifyPing(timestamp, pingNumber));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -274,43 +274,63 @@ public class ExamAPI_V1_Controller {
|
||||||
.ok()
|
.ok()
|
||||||
.build();
|
.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(
|
@RequestMapping(
|
||||||
path = API.EXAM_API_PING_ENDPOINT,
|
path = API.EXAM_API_PING_ENDPOINT,
|
||||||
method = RequestMethod.POST,
|
method = RequestMethod.POST,
|
||||||
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
|
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
|
||||||
produces = MediaType.APPLICATION_JSON_UTF8_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,
|
@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_TIMESTAMP, required = true) final long timestamp,
|
||||||
@RequestParam(name = API.EXAM_API_PING_NUMBER, required = false) final int pingNumber) {
|
@RequestParam(name = API.EXAM_API_PING_NUMBER, required = false) final int pingNumber) {
|
||||||
|
|
||||||
return CompletableFuture.supplyAsync(
|
final String instruction = this.sebClientConnectionService
|
||||||
() -> {
|
.notifyPing(connectionToken, timestamp, pingNumber);
|
||||||
final String notifyPing = this.sebClientConnectionService
|
|
||||||
.notifyPing(connectionToken, timestamp, pingNumber);
|
|
||||||
if (notifyPing == null) {
|
|
||||||
return EMPTY_PING_RESPONSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ResponseEntity
|
if (instruction == null) {
|
||||||
.ok()
|
return EMPTY_PING_RESPONSE;
|
||||||
.body(notifyPing);
|
}
|
||||||
},
|
|
||||||
this.executor);
|
return ResponseEntity
|
||||||
|
.ok()
|
||||||
|
.body(instruction);
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequestMapping(
|
@RequestMapping(
|
||||||
path = API.EXAM_API_EVENT_ENDPOINT,
|
path = API.EXAM_API_EVENT_ENDPOINT,
|
||||||
method = RequestMethod.POST,
|
method = RequestMethod.POST,
|
||||||
consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
|
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,
|
@RequestHeader(name = API.EXAM_API_SEB_CONNECTION_TOKEN, required = true) final String connectionToken,
|
||||||
@RequestBody(required = true) final ClientEvent event) {
|
@RequestBody(required = true) final ClientEvent event) {
|
||||||
|
|
||||||
return CompletableFuture.runAsync(
|
this.sebClientConnectionService
|
||||||
() -> this.sebClientConnectionService
|
.notifyClientEvent(connectionToken, event);
|
||||||
.notifyClientEvent(connectionToken, event),
|
|
||||||
this.executor);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Long getInstitutionId(final Principal principal) {
|
private Long getInstitutionId(final Principal principal) {
|
||||||
|
|
|
@ -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.EntityType;
|
||||||
import ch.ethz.seb.sebserver.gbl.api.POSTMapper;
|
import ch.ethz.seb.sebserver.gbl.api.POSTMapper;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.Domain;
|
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.GrantEntity;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator;
|
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator;
|
||||||
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
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.gbl.util.Result;
|
||||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.IndicatorRecordDynamicSqlSupport;
|
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.IndicatorRecordDynamicSqlSupport;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.PaginationService;
|
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.ExamDAO;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.IndicatorDAO;
|
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.dao.UserActivityLogDAO;
|
||||||
|
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamSessionService;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationService;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationService;
|
||||||
|
|
||||||
@WebServiceProfile
|
@WebServiceProfile
|
||||||
|
@ -35,6 +38,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationSe
|
||||||
public class IndicatorController extends EntityController<Indicator, Indicator> {
|
public class IndicatorController extends EntityController<Indicator, Indicator> {
|
||||||
|
|
||||||
private final ExamDAO examDao;
|
private final ExamDAO examDao;
|
||||||
|
private final ExamSessionService examSessionService;
|
||||||
|
|
||||||
protected IndicatorController(
|
protected IndicatorController(
|
||||||
final AuthorizationService authorization,
|
final AuthorizationService authorization,
|
||||||
|
@ -43,7 +47,8 @@ public class IndicatorController extends EntityController<Indicator, Indicator>
|
||||||
final ExamDAO examDao,
|
final ExamDAO examDao,
|
||||||
final UserActivityLogDAO userActivityLogDAO,
|
final UserActivityLogDAO userActivityLogDAO,
|
||||||
final PaginationService paginationService,
|
final PaginationService paginationService,
|
||||||
final BeanValidationService beanValidationService) {
|
final BeanValidationService beanValidationService,
|
||||||
|
final ExamSessionService examSessionService) {
|
||||||
|
|
||||||
super(authorization,
|
super(authorization,
|
||||||
bulkActionService,
|
bulkActionService,
|
||||||
|
@ -53,6 +58,7 @@ public class IndicatorController extends EntityController<Indicator, Indicator>
|
||||||
beanValidationService);
|
beanValidationService);
|
||||||
|
|
||||||
this.examDao = examDao;
|
this.examDao = examDao;
|
||||||
|
this.examSessionService = examSessionService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -95,4 +101,31 @@ public class IndicatorController extends EntityController<Indicator, Indicator>
|
||||||
return EntityType.EXAM;
|
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());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -259,12 +259,8 @@ public abstract class ExamAPIIntegrationTester {
|
||||||
+ "&" + API.EXAM_API_PING_NUMBER + "=" + num;
|
+ "&" + API.EXAM_API_PING_NUMBER + "=" + num;
|
||||||
builder.content(body);
|
builder.content(body);
|
||||||
|
|
||||||
final MvcResult mvcResult = this.mockMvc
|
final ResultActions result = this.mockMvc
|
||||||
.perform(builder)
|
.perform(builder);
|
||||||
.andExpect(request().asyncStarted())
|
|
||||||
.andDo(MockMvcResultHandlers.log())
|
|
||||||
.andReturn();
|
|
||||||
final ResultActions result = this.mockMvc.perform(asyncDispatch(mvcResult));
|
|
||||||
return result.andReturn().getResponse();
|
return result.andReturn().getResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -284,12 +280,8 @@ public abstract class ExamAPIIntegrationTester {
|
||||||
|
|
||||||
final String body = "{ \"type\": \"%s\", \"timestamp\": %s, \"numericValue\": %s, \"text\": \"%s\" }";
|
final String body = "{ \"type\": \"%s\", \"timestamp\": %s, \"numericValue\": %s, \"text\": \"%s\" }";
|
||||||
builder.content(String.format(body, type, timestamp, value, text));
|
builder.content(String.format(body, type, timestamp, value, text));
|
||||||
final MvcResult mvcResult = this.mockMvc
|
final ResultActions result = this.mockMvc
|
||||||
.perform(builder)
|
.perform(builder);
|
||||||
.andExpect(request().asyncStarted())
|
|
||||||
.andDo(MockMvcResultHandlers.log())
|
|
||||||
.andReturn();
|
|
||||||
final ResultActions result = this.mockMvc.perform(asyncDispatch(mvcResult));
|
|
||||||
return result.andReturn().getResponse();
|
return result.andReturn().getResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue