From 3c90260fba780f1efd3aa8db8b3a028d57cbcb86 Mon Sep 17 00:00:00 2001 From: anhefti Date: Thu, 1 Jun 2023 16:17:54 +0200 Subject: [PATCH] code cleanup --- .../session/EventHandlingStrategy.java | 30 --- .../impl/AsyncBatchEventSaveStrategy.java | 238 ------------------ .../impl/EventHandlingStrategyFactory.java | 41 --- .../session/impl/SingleEventSaveStrategy.java | 110 -------- 4 files changed, 419 deletions(-) delete mode 100644 src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/EventHandlingStrategy.java delete mode 100644 src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/AsyncBatchEventSaveStrategy.java delete mode 100644 src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/EventHandlingStrategyFactory.java delete mode 100644 src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/SingleEventSaveStrategy.java diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/EventHandlingStrategy.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/EventHandlingStrategy.java deleted file mode 100644 index 2ef1ee28..00000000 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/EventHandlingStrategy.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2018 ETH Zürich, Educational Development and Technology (LET) - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -package ch.ethz.seb.sebserver.webservice.servicelayer.session; - -import java.util.function.Consumer; - -import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.ClientEventRecord; - -/** A exam session SEB client event handling strategy implements a certain strategy to - * store ClientEvent that are coming in within the specified endpoint in height frequency. */ -@Deprecated -public interface EventHandlingStrategy extends Consumer { - - String EVENT_CONSUMER_STRATEGY_CONFIG_PROPERTY_KEY = "sebserver.webservice.api.exam.event-handling-strategy"; - String EVENT_CONSUMER_STRATEGY_VALUE_PROPERTY = - "${" + EVENT_CONSUMER_STRATEGY_CONFIG_PROPERTY_KEY + ":SINGLE_EVENT_STORE_STRATEGY}"; - String EVENT_CONSUMER_STRATEGY_SINGLE_EVENT_STORE = "SINGLE_EVENT_STORE_STRATEGY"; - String EVENT_CONSUMER_STRATEGY_ASYNC_BATCH_STORE = "ASYNC_BATCH_STORE_STRATEGY"; - - /** This enables a certain EventHandlingStrategy to be the executing and EventHandlingStrategy - * and will be re-initialized on server restart */ - void enable(); - -} diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/AsyncBatchEventSaveStrategy.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/AsyncBatchEventSaveStrategy.java deleted file mode 100644 index 27c35222..00000000 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/AsyncBatchEventSaveStrategy.java +++ /dev/null @@ -1,238 +0,0 @@ -/* - * Copyright (c) 2018 ETH Zürich, Educational Development and Technology (LET) - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -package ch.ethz.seb.sebserver.webservice.servicelayer.session.impl; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.concurrent.BlockingDeque; -import java.util.concurrent.Executor; -import java.util.concurrent.LinkedBlockingDeque; - -import javax.annotation.PreDestroy; - -import org.apache.ibatis.session.ExecutorType; -import org.apache.ibatis.session.SqlSessionFactory; -import org.mybatis.spring.SqlSessionTemplate; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.context.annotation.Lazy; -import org.springframework.context.event.EventListener; -import org.springframework.stereotype.Component; -import org.springframework.transaction.PlatformTransactionManager; -import org.springframework.transaction.TransactionDefinition; -import org.springframework.transaction.support.TransactionTemplate; - -import ch.ethz.seb.sebserver.SEBServerInit; -import ch.ethz.seb.sebserver.SEBServerInitEvent; -import ch.ethz.seb.sebserver.gbl.Constants; -import ch.ethz.seb.sebserver.gbl.async.AsyncServiceSpringConfig; -import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent.EventType; -import ch.ethz.seb.sebserver.gbl.model.session.ClientNotification; -import ch.ethz.seb.sebserver.gbl.model.session.ClientNotification.NotificationType; -import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; -import ch.ethz.seb.sebserver.gbl.util.Pair; -import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ClientEventRecordMapper; -import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.ClientEventRecord; -import ch.ethz.seb.sebserver.webservice.servicelayer.session.EventHandlingStrategy; -import ch.ethz.seb.sebserver.webservice.servicelayer.session.SEBClientNotificationService; - -/** Approach 2 to handle/save client events internally - * - * This Approach uses a queue to collect ClientEvents that are stored later. The queue is shared between some - * worker-threads that batch gets and stores the events from the queue afterwards. this approach is less blocking from - * the caller perspective and also faster on store data by using bulk-insert - * - * A disadvantage is an potentially multiple event data loss on total server fail. The data in the queue is state that - * is not stored somewhere yet and can't be recovered on total server fail. - * - * If the performance of this approach is not enough or the potentially data loss on total server fail is a risk that - * not can be taken, we have to consider using a messaging system/server like rabbitMQ or Apache-Kafka that brings the - * ability to effectively store and recover message queues but also comes with more complexity on setup and installation - * side as well as for the whole server system. */ -@Lazy -@Component(EventHandlingStrategy.EVENT_CONSUMER_STRATEGY_ASYNC_BATCH_STORE) -@WebServiceProfile -public class AsyncBatchEventSaveStrategy implements EventHandlingStrategy { - - private static final Logger log = LoggerFactory.getLogger(AsyncBatchEventSaveStrategy.class); - - private static final int NUMBER_OF_WORKER_THREADS = 4; - private static final int BATCH_SIZE = 100; - private static final int MIN_SLEEP_TIME = 100; - private static final int SLEEP_TIME_EXPAND = 100; - private static final int MAX_SLEEP_TIME = 5000; - - private final SEBClientNotificationService sebClientNotificationService; - private final SqlSessionFactory sqlSessionFactory; - private final Executor executor; - private final TransactionTemplate transactionTemplate; - - private final BlockingDeque eventQueue = new LinkedBlockingDeque<>(); - private final BlockingDeque notificationQueue = new LinkedBlockingDeque<>(); - private boolean workersRunning = false; - private final boolean enabled = false; - - public AsyncBatchEventSaveStrategy( - final SEBClientNotificationService sebClientNotificationService, - final SqlSessionFactory sqlSessionFactory, - final PlatformTransactionManager transactionManager, - @Qualifier(AsyncServiceSpringConfig.EXAM_API_EXECUTOR_BEAN_NAME) final Executor executor) { - - this.sebClientNotificationService = sebClientNotificationService; - this.sqlSessionFactory = sqlSessionFactory; - this.executor = executor; - - this.transactionTemplate = new TransactionTemplate(transactionManager); - this.transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); - } - - @Override - public void enable() { - log.info("AsyncBatchEventSaveStrategy is deprecated"); - //this.enabled = true; - } - - @EventListener(SEBServerInitEvent.class) - protected void recover() { - if (this.enabled) { - SEBServerInit.INIT_LOGGER.info("------>"); - SEBServerInit.INIT_LOGGER.info("------> Start {} Event-Batch-Store Worker-Threads", - NUMBER_OF_WORKER_THREADS); - - runWorkers(); - - try { - Thread.sleep(Constants.SECOND_IN_MILLIS / 2); - } catch (final Exception e) { - log.error("Failed to wait"); - } - } - } - - @PreDestroy - protected void shutdown() { - log.info("Reset workersRunning flag to stop worker after event queue is empty"); - this.workersRunning = false; - } - - @Override - public void accept(final ClientEventRecord record) { - if (record == null || !this.workersRunning) { - return; - } - - if (EventType.isNotificationEvent(record.getType())) { - final Pair typeAndPlainText = - ClientNotification.extractTypeAndPlainText(record.getText()); - this.notificationQueue.add(new ClientNotification( - record.getId(), - record.getClientConnectionId(), - EventType.byId(record.getType()), - record.getClientTime(), - record.getServerTime(), - (record.getNumericValue() != null) ? record.getNumericValue().doubleValue() : null, - typeAndPlainText.b, - typeAndPlainText.a)); - } else { - this.eventQueue.add(record); - } - } - - private void runWorkers() { - if (this.workersRunning) { - log.warn("runWorkers called when workers are running already. Ignore that"); - return; - } - - this.workersRunning = true; - for (int i = 0; i < NUMBER_OF_WORKER_THREADS; i++) { - this.executor.execute(batchSave()); - } - } - - private Runnable batchSave() { - return () -> { - - SEBServerInit.INIT_LOGGER.info("> Worker Thread {} running", Thread.currentThread()); - - final Collection events = new ArrayList<>(); - @SuppressWarnings("resource") - final SqlSessionTemplate sqlSessionTemplate = new SqlSessionTemplate( - this.sqlSessionFactory, - ExecutorType.BATCH); - final ClientEventRecordMapper clientEventMapper = sqlSessionTemplate.getMapper( - ClientEventRecordMapper.class); - - long sleepTime = MIN_SLEEP_TIME; - - try { - while (this.workersRunning) { - events.clear(); - this.eventQueue.drainTo(events, BATCH_SIZE); - - // batch store log events - try { - if (!events.isEmpty()) { - sleepTime = MIN_SLEEP_TIME; - this.transactionTemplate - .execute(status -> { - events.forEach(clientEventMapper::insert); - return null; - }); - - sqlSessionTemplate.flushStatements(); - } else if (sleepTime < MAX_SLEEP_TIME) { - sleepTime += SLEEP_TIME_EXPAND; - } - } catch (final Exception e) { - log.error("unexpected Error while trying to batch store client-events: ", e); - } - - // store notification events - if (!this.notificationQueue.isEmpty()) { - try { - - final ClientNotification notification = this.notificationQueue.poll(); - switch (notification.eventType) { - case NOTIFICATION: { - this.sebClientNotificationService.newNotification(notification); - break; - } - case NOTIFICATION_CONFIRMED: { - this.sebClientNotificationService.confirmPendingNotification(notification); - break; - } - default: - } - } catch (final Exception e) { - log.error("unexpected Error while trying to store client-notification: ", e); - } - } - - try { - Thread.sleep(sleepTime); - } catch (final InterruptedException e) { - e.printStackTrace(); - } - } - } finally { - try { - sqlSessionTemplate.destroy(); - } catch (final Exception e) { - log.error("Failed to close and destroy the SqlSessionTemplate for this thread: {}", - Thread.currentThread(), - e); - } - log.debug("Worker Thread {} stopped", Thread.currentThread()); - } - }; - } - -} diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/EventHandlingStrategyFactory.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/EventHandlingStrategyFactory.java deleted file mode 100644 index b507d647..00000000 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/EventHandlingStrategyFactory.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET) - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -package ch.ethz.seb.sebserver.webservice.servicelayer.session.impl; - -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.ApplicationContext; -import org.springframework.context.annotation.Lazy; -import org.springframework.stereotype.Service; - -import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; -import ch.ethz.seb.sebserver.webservice.servicelayer.session.EventHandlingStrategy; - -@Lazy -@Service -@WebServiceProfile -@Deprecated -public class EventHandlingStrategyFactory { - - private final EventHandlingStrategy eventHandlingStrategy; - - protected EventHandlingStrategyFactory( - final ApplicationContext applicationContext, - @Value(EventHandlingStrategy.EVENT_CONSUMER_STRATEGY_VALUE_PROPERTY) final String nameProperty) { - - this.eventHandlingStrategy = applicationContext.getBean( - nameProperty, - EventHandlingStrategy.class); - this.eventHandlingStrategy.enable(); - } - - public EventHandlingStrategy get() { - return this.eventHandlingStrategy; - } - -} diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/SingleEventSaveStrategy.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/SingleEventSaveStrategy.java deleted file mode 100644 index e741647c..00000000 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/SingleEventSaveStrategy.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (c) 2018 ETH Zürich, Educational Development and Technology (LET) - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -package ch.ethz.seb.sebserver.webservice.servicelayer.session.impl; - -import org.springframework.context.annotation.Lazy; -import org.springframework.context.event.EventListener; -import org.springframework.stereotype.Component; -import org.springframework.transaction.annotation.Transactional; - -import ch.ethz.seb.sebserver.SEBServerInit; -import ch.ethz.seb.sebserver.SEBServerInitEvent; -import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent.EventType; -import ch.ethz.seb.sebserver.gbl.model.session.ClientNotification; -import ch.ethz.seb.sebserver.gbl.model.session.ClientNotification.NotificationType; -import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; -import ch.ethz.seb.sebserver.gbl.util.Pair; -import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ClientEventRecordMapper; -import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.ClientEventRecord; -import ch.ethz.seb.sebserver.webservice.servicelayer.session.EventHandlingStrategy; -import ch.ethz.seb.sebserver.webservice.servicelayer.session.SEBClientNotificationService; - -/** Approach 1 to handle/save client events internally - * - * This saves one on one client event to persistence within separated transaction. - * NOTE: if there are a lot of clients connected, firing events at small intervals like 100ms, - * this is blocking to much because every event is saved within its own SQL commit and also - * in its own transaction. - * - * An advantage of this approach is minimal data loss on server fail. **/ -@Lazy -@Component(EventHandlingStrategy.EVENT_CONSUMER_STRATEGY_SINGLE_EVENT_STORE) -@WebServiceProfile -public class SingleEventSaveStrategy implements EventHandlingStrategy { - - private final SEBClientNotificationService sebClientNotificationService; - private final ClientEventRecordMapper clientEventRecordMapper; - private boolean enabled = false; - - public SingleEventSaveStrategy( - final SEBClientNotificationService sebClientNotificationService, - final ClientEventRecordMapper clientEventRecordMapper) { - - this.sebClientNotificationService = sebClientNotificationService; - this.clientEventRecordMapper = clientEventRecordMapper; - } - - @EventListener(SEBServerInitEvent.class) - public void init() { - if (this.enabled) { - SEBServerInit.INIT_LOGGER.info("------>"); - SEBServerInit.INIT_LOGGER.info("------> Run SingleEventSaveStrategy for SEB event handling"); - } - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void accept(final ClientEventRecord record) { - - if (EventType.isNotificationEvent(record.getType())) { - final Pair typeAndPlainText = - ClientNotification.extractTypeAndPlainText(record.getText()); - - final ClientNotification clientNotification = new ClientNotification( - record.getId(), - record.getClientConnectionId(), - EventType.byId(record.getType()), - record.getClientTime(), - record.getServerTime(), - (record.getNumericValue() != null) ? record.getNumericValue().doubleValue() : null, - typeAndPlainText.b, - typeAndPlainText.a); - - switch (clientNotification.eventType) { - case NOTIFICATION: { - this.sebClientNotificationService.newNotification(clientNotification); - break; - } - case NOTIFICATION_CONFIRMED: { - this.sebClientNotificationService.confirmPendingNotification(clientNotification); - break; - } - default: - } - - } else { - if (record.getId() == null) { - this.clientEventRecordMapper.insert(record); - } else { - this.clientEventRecordMapper.updateByPrimaryKeySelective(record); - } - } - } - - @Override - public void enable() { - this.enabled = true; - - } - - public boolean isEnabled() { - return this.enabled; - } - -}