log SEB Client ping threshold overflows as Error log
This commit is contained in:
		
							parent
							
								
									3fee5dc0a6
								
							
						
					
					
						commit
						b27c79e1d8
					
				
					 8 changed files with 148 additions and 7 deletions
				
			
		|  | @ -175,7 +175,7 @@ public final class Indicator implements Entity { | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public static final class Threshold { | ||||
|     public static final class Threshold implements Comparable<Threshold> { | ||||
| 
 | ||||
|         @JsonProperty(THRESHOLD.ATTR_VALUE) | ||||
|         @NotNull | ||||
|  | @ -206,6 +206,11 @@ public final class Indicator implements Entity { | |||
|             return "Threshold [value=" + this.value + ", color=" + this.color + "]"; | ||||
|         } | ||||
| 
 | ||||
|         @Override | ||||
|         public int compareTo(final Threshold o) { | ||||
|             return Double.compare(this.value, (o != null) ? o.value : 0); | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -71,6 +71,11 @@ public interface ExamDAO extends ActivatableEntityDAO<Exam, Exam>, BulkActionSup | |||
|      * @return Result refer to a collection of exams or to an error if happened */ | ||||
|     Result<Collection<Exam>> allForEndCheck(); | ||||
| 
 | ||||
|     /** Get a collection of all currently running exam identifiers | ||||
|      * | ||||
|      * @return collection of all currently running exam identifiers */ | ||||
|     Result<Collection<Long>> allRunningExamIds(); | ||||
| 
 | ||||
|     /** This is used to place an internal (write)lock for the specified exam. | ||||
|      * The exam will be marked as locked on the persistence level to prevent other running web-service instances | ||||
|      * to write concurrently to the specified exam while it is been updated by an internal batch process. | ||||
|  |  | |||
|  | @ -353,6 +353,26 @@ public class ExamDAOImpl implements ExamDAO { | |||
|                 .execute()); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     @Transactional(readOnly = true) | ||||
|     public Result<Collection<Long>> allRunningExamIds() { | ||||
|         return Result.tryCatch(() -> { | ||||
|             return this.examRecordMapper.selectIdsByExample() | ||||
|                     .where( | ||||
|                             ExamRecordDynamicSqlSupport.active, | ||||
|                             isEqualTo(BooleanUtils.toInteger(true))) | ||||
|                     .and( | ||||
|                             ExamRecordDynamicSqlSupport.status, | ||||
|                             isEqualTo(ExamStatus.RUNNING.name())) | ||||
|                     .and( | ||||
|                             ExamRecordDynamicSqlSupport.updating, | ||||
|                             isEqualTo(BooleanUtils.toInteger(false))) | ||||
| 
 | ||||
|                     .build() | ||||
|                     .execute(); | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     @Transactional(readOnly = true) | ||||
|     public Result<Collection<Exam>> allForRunCheck() { | ||||
|  |  | |||
|  | @ -103,6 +103,8 @@ public interface SebClientConnectionService { | |||
|             Long institutionId, | ||||
|             String clientAddress); | ||||
| 
 | ||||
|     void updatePingEvents(); | ||||
| 
 | ||||
|     /** Notify a ping for a certain client connection. | ||||
|      * | ||||
|      * @param connectionToken the connection token | ||||
|  |  | |||
|  | @ -23,6 +23,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore; | |||
| import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent.EventType; | ||||
| import ch.ethz.seb.sebserver.webservice.datalayer.batis.ClientEventExtentionMapper; | ||||
| import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ClientEventRecordDynamicSqlSupport; | ||||
| import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.ClientEventRecord; | ||||
| 
 | ||||
| public abstract class AbstractPingIndicator extends AbstractClientIndicator { | ||||
| 
 | ||||
|  | @ -84,4 +85,6 @@ public abstract class AbstractPingIndicator extends AbstractClientIndicator { | |||
|         return this.pingNumber; | ||||
|     } | ||||
| 
 | ||||
|     public abstract ClientEventRecord updateLogEvent(); | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -20,8 +20,10 @@ import org.springframework.scheduling.annotation.Async; | |||
| import org.springframework.scheduling.annotation.Scheduled; | ||||
| import org.springframework.stereotype.Service; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gbl.Constants; | ||||
| import ch.ethz.seb.sebserver.gbl.model.exam.Exam; | ||||
| import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamDAO; | ||||
| import ch.ethz.seb.sebserver.webservice.servicelayer.session.SebClientConnectionService; | ||||
| 
 | ||||
| @Service | ||||
| class ExamSessionControlTask { | ||||
|  | @ -29,6 +31,7 @@ class ExamSessionControlTask { | |||
|     private static final Logger log = LoggerFactory.getLogger(ExamSessionControlTask.class); | ||||
| 
 | ||||
|     private final ExamDAO examDAO; | ||||
|     private final SebClientConnectionService sebClientConnectionService; | ||||
|     private final ExamUpdateHandler examUpdateHandler; | ||||
|     private final Long examTimePrefix; | ||||
|     private final Long examTimeSuffix; | ||||
|  | @ -46,20 +49,21 @@ class ExamSessionControlTask { | |||
| 
 | ||||
|     protected ExamSessionControlTask( | ||||
|             final ExamDAO examDAO, | ||||
|             final SebClientConnectionService sebClientConnectionService, | ||||
|             final ExamUpdateHandler examUpdateHandler, | ||||
|             @Value("${sebserver.webservice.api.exam.time-prefix:3600000}") final Long examTimePrefix, | ||||
|             @Value("${sebserver.webservice.api.exam.time-suffix:3600000}") final Long examTimeSuffix) { | ||||
| 
 | ||||
|         this.examDAO = examDAO; | ||||
|         this.sebClientConnectionService = sebClientConnectionService; | ||||
|         this.examUpdateHandler = examUpdateHandler; | ||||
|         this.examTimePrefix = examTimePrefix; | ||||
|         this.examTimeSuffix = examTimeSuffix; | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     @Async | ||||
|     @Scheduled(cron = "${sebserver.webservice.api.exam.update-interval:1 * * * * *}") | ||||
|     public void execTask() { | ||||
|     public void examRunUpdateTask() { | ||||
| 
 | ||||
|         final String updateId = this.examUpdateHandler.createUpdateId(); | ||||
| 
 | ||||
|  | @ -67,11 +71,17 @@ class ExamSessionControlTask { | |||
|             log.debug("Run exam runtime update task with Id: {}", updateId); | ||||
|         } | ||||
| 
 | ||||
|         controlStart(updateId); | ||||
|         controlEnd(updateId); | ||||
|         controlExamStart(updateId); | ||||
|         controlExamEnd(updateId); | ||||
|     } | ||||
| 
 | ||||
|     private void controlStart(final String updateId) { | ||||
|     @Async | ||||
|     @Scheduled(fixedRate = Constants.SECOND_IN_MILLIS) | ||||
|     public void pingEventUpdateTask() { | ||||
|         this.sebClientConnectionService.updatePingEvents(); | ||||
|     } | ||||
| 
 | ||||
|     private void controlExamStart(final String updateId) { | ||||
|         if (log.isDebugEnabled()) { | ||||
|             log.debug("Check starting exams: {}", updateId); | ||||
|         } | ||||
|  | @ -95,7 +105,7 @@ class ExamSessionControlTask { | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private void controlEnd(final String updateId) { | ||||
|     private void controlExamEnd(final String updateId) { | ||||
|         if (log.isDebugEnabled()) { | ||||
|             log.debug("Check ending exams: {}", updateId); | ||||
|         } | ||||
|  |  | |||
|  | @ -8,29 +8,60 @@ | |||
| 
 | ||||
| package ch.ethz.seb.sebserver.webservice.servicelayer.session.impl; | ||||
| 
 | ||||
| import java.math.BigDecimal; | ||||
| import java.util.Comparator; | ||||
| 
 | ||||
| import org.joda.time.DateTime; | ||||
| import org.joda.time.DateTimeZone; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
| import org.springframework.beans.factory.config.ConfigurableBeanFactory; | ||||
| import org.springframework.context.annotation.Lazy; | ||||
| import org.springframework.context.annotation.Scope; | ||||
| import org.springframework.stereotype.Component; | ||||
| 
 | ||||
| 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.IndicatorType; | ||||
| import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent.EventType; | ||||
| import ch.ethz.seb.sebserver.gbl.model.session.IndicatorValueHolder; | ||||
| import ch.ethz.seb.sebserver.gbl.util.Utils; | ||||
| import ch.ethz.seb.sebserver.webservice.datalayer.batis.ClientEventExtentionMapper; | ||||
| import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.ClientEventRecord; | ||||
| 
 | ||||
| @Lazy | ||||
| @Component(IndicatorType.Names.LAST_PING) | ||||
| @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) | ||||
| public final class PingIntervalClientIndicator extends AbstractPingIndicator { | ||||
| 
 | ||||
|     private static final Logger log = LoggerFactory.getLogger(PingIntervalClientIndicator.class); | ||||
| 
 | ||||
|     private long pingErrorThreshold; | ||||
|     private boolean isOnError = false; | ||||
| 
 | ||||
|     public PingIntervalClientIndicator(final ClientEventExtentionMapper clientEventExtentionMapper) { | ||||
|         super(clientEventExtentionMapper); | ||||
|         this.cachingEnabled = true; | ||||
|         this.currentValue = computeValueAt(Utils.getMillisecondsNow()); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void init(final Indicator indicatorDefinition, final Long connectionId, final boolean cachingEnabled) { | ||||
|         super.init(indicatorDefinition, connectionId, cachingEnabled); | ||||
| 
 | ||||
|         try { | ||||
|             indicatorDefinition | ||||
|                     .getThresholds() | ||||
|                     .stream() | ||||
|                     .sorted(Comparator.reverseOrder()) | ||||
|                     .findFirst() | ||||
|                     .ifPresent(t -> this.pingErrorThreshold = t.value.longValue()); | ||||
|         } catch (final Exception e) { | ||||
|             log.error("Failed to initialize pingErrorThreshold: {}", e.getMessage()); | ||||
|             this.pingErrorThreshold = Constants.SECOND_IN_MILLIS * 5; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public IndicatorType getType() { | ||||
|         return IndicatorType.LAST_PING; | ||||
|  | @ -47,4 +78,37 @@ public final class PingIntervalClientIndicator extends AbstractPingIndicator { | |||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public ClientEventRecord updateLogEvent() { | ||||
|         final long now = DateTime.now(DateTimeZone.UTC).getMillis(); | ||||
|         final long value = now - (long) super.currentValue; | ||||
|         if (this.isOnError) { | ||||
|             if (this.pingErrorThreshold > value) { | ||||
|                 this.isOnError = false; | ||||
|                 return new ClientEventRecord( | ||||
|                         null, | ||||
|                         this.connectionId, | ||||
|                         EventType.INFO_LOG.id, | ||||
|                         now, | ||||
|                         now, | ||||
|                         new BigDecimal(value), | ||||
|                         "Client Ping Back To Normal"); | ||||
|             } | ||||
|         } else { | ||||
|             if (this.pingErrorThreshold < value) { | ||||
|                 this.isOnError = true; | ||||
|                 return new ClientEventRecord( | ||||
|                         null, | ||||
|                         this.connectionId, | ||||
|                         EventType.ERROR_LOG.id, | ||||
|                         now, | ||||
|                         now, | ||||
|                         new BigDecimal(value), | ||||
|                         "Missing Client Ping"); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return null; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -9,11 +9,14 @@ | |||
| package ch.ethz.seb.sebserver.webservice.servicelayer.session.impl; | ||||
| 
 | ||||
| import java.security.Principal; | ||||
| import java.util.Objects; | ||||
| import java.util.UUID; | ||||
| 
 | ||||
| import org.apache.commons.lang3.StringUtils; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
| import org.springframework.cache.Cache; | ||||
| import org.springframework.cache.CacheManager; | ||||
| import org.springframework.context.annotation.Lazy; | ||||
| import org.springframework.stereotype.Service; | ||||
| 
 | ||||
|  | @ -43,6 +46,7 @@ public class SebClientConnectionServiceImpl implements SebClientConnectionServic | |||
| 
 | ||||
|     private final ExamSessionService examSessionService; | ||||
|     private final ExamSessionCacheService examSessionCacheService; | ||||
|     private final CacheManager cacheManager; | ||||
|     private final EventHandlingStrategy eventHandlingStrategy; | ||||
|     private final ClientConnectionDAO clientConnectionDAO; | ||||
|     private final PingHandlingStrategy pingHandlingStrategy; | ||||
|  | @ -52,6 +56,7 @@ public class SebClientConnectionServiceImpl implements SebClientConnectionServic | |||
|     protected SebClientConnectionServiceImpl( | ||||
|             final ExamSessionService examSessionService, | ||||
|             final ExamSessionCacheService examSessionCacheService, | ||||
|             final CacheManager cacheManager, | ||||
|             final ClientConnectionDAO clientConnectionDAO, | ||||
|             final EventHandlingStrategyFactory eventHandlingStrategyFactory, | ||||
|             final PingHandlingStrategyFactory pingHandlingStrategyFactory, | ||||
|  | @ -60,6 +65,7 @@ public class SebClientConnectionServiceImpl implements SebClientConnectionServic | |||
| 
 | ||||
|         this.examSessionService = examSessionService; | ||||
|         this.examSessionCacheService = examSessionCacheService; | ||||
|         this.cacheManager = cacheManager; | ||||
|         this.clientConnectionDAO = clientConnectionDAO; | ||||
|         this.pingHandlingStrategy = pingHandlingStrategyFactory.get(); | ||||
|         this.eventHandlingStrategy = eventHandlingStrategyFactory.get(); | ||||
|  | @ -382,6 +388,32 @@ public class SebClientConnectionServiceImpl implements SebClientConnectionServic | |||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void updatePingEvents() { | ||||
|         try { | ||||
| 
 | ||||
|             final Cache cache = this.cacheManager.getCache(ExamSessionCacheService.CACHE_NAME_ACTIVE_CLIENT_CONNECTION); | ||||
|             this.examSessionService | ||||
|                     .getExamDAO() | ||||
|                     .allRunningExamIds() | ||||
|                     .getOrThrow() | ||||
|                     .stream() | ||||
|                     .flatMap(examId -> this.clientConnectionDAO | ||||
|                             .getConnectionTokens(examId) | ||||
|                             .getOrThrow() | ||||
|                             .stream()) | ||||
|                     .map(token -> cache.get(token, ClientConnectionDataInternal.class)) | ||||
|                     .filter(Objects::nonNull) | ||||
|                     .flatMap(connection -> connection.pingMappings.stream()) | ||||
|                     .map(ping -> ping.updateLogEvent()) | ||||
|                     .filter(Objects::nonNull) | ||||
|                     .forEach(this.eventHandlingStrategy::accept); | ||||
| 
 | ||||
|         } catch (final Exception e) { | ||||
|             log.error("Failed to update ping events: ", e); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public String notifyPing( | ||||
|             final String connectionToken, | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 anhefti
						anhefti