From 898cb61d0b375643eb05ac5fe8ded7fafb795f20 Mon Sep 17 00:00:00 2001 From: Stephan Krusche Date: Sat, 25 May 2024 15:16:20 +0200 Subject: [PATCH 1/4] Development: Improve boundary cases with Hazelcast --- .../service/SharedQueueProcessingService.java | 13 +++++++----- .../ParticipationTeamWebsocketService.java | 20 +++++++++++-------- .../LocalCIResultProcessingService.java | 9 ++++++--- 3 files changed, 26 insertions(+), 16 deletions(-) diff --git a/src/main/java/de/tum/cit/aet/artemis/buildagent/service/SharedQueueProcessingService.java b/src/main/java/de/tum/cit/aet/artemis/buildagent/service/SharedQueueProcessingService.java index f82f7aa7a36f..264d8e581707 100644 --- a/src/main/java/de/tum/cit/aet/artemis/buildagent/service/SharedQueueProcessingService.java +++ b/src/main/java/de/tum/cit/aet/artemis/buildagent/service/SharedQueueProcessingService.java @@ -17,13 +17,14 @@ import java.util.concurrent.locks.ReentrantLock; import java.util.stream.Collectors; -import jakarta.annotation.PostConstruct; import jakarta.annotation.PreDestroy; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.context.annotation.Profile; +import org.springframework.context.event.EventListener; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; @@ -94,7 +95,7 @@ public SharedQueueProcessingService(@Qualifier("hazelcastInstance") HazelcastIns /** * Initialize relevant data from hazelcast */ - @PostConstruct + @EventListener(ApplicationReadyEvent.class) public void init() { this.buildAgentInformation = this.hazelcastInstance.getMap("buildAgentInformation"); this.processingJobs = this.hazelcastInstance.getMap("processingJobs"); @@ -105,7 +106,9 @@ public void init() { @PreDestroy public void removeListener() { - this.queue.removeItemListener(this.listenerId); + if (hazelcastInstance.getLifecycleService().isRunning()) { + this.queue.removeItemListener(this.listenerId); + } } /** @@ -133,7 +136,7 @@ public void updateBuildAgentInformation() { * This is a backup mechanism in case the build queue is not empty, no new build jobs are entering the queue and the * node otherwise stopped checking for build jobs in the queue. */ - @Scheduled(fixedRate = 10000) + @Scheduled(fixedRate = 10000, initialDelay = 5000) public void checkForBuildJobs() { checkAvailabilityAndProcessNextBuild(); } @@ -143,7 +146,7 @@ public void checkForBuildJobs() { * If so, process the next build job. */ private void checkAvailabilityAndProcessNextBuild() { - if (noDataMemberInClusterAvailable(hazelcastInstance)) { + if (noDataMemberInClusterAvailable(hazelcastInstance) || queue == null) { log.debug("There are only lite member in the cluster. Not processing build jobs."); return; } diff --git a/src/main/java/de/tum/cit/aet/artemis/exercise/web/ParticipationTeamWebsocketService.java b/src/main/java/de/tum/cit/aet/artemis/exercise/web/ParticipationTeamWebsocketService.java index ccf10fb60273..8f8d0138e3ab 100644 --- a/src/main/java/de/tum/cit/aet/artemis/exercise/web/ParticipationTeamWebsocketService.java +++ b/src/main/java/de/tum/cit/aet/artemis/exercise/web/ParticipationTeamWebsocketService.java @@ -10,11 +10,10 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import jakarta.annotation.PostConstruct; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.context.annotation.Profile; import org.springframework.context.event.EventListener; import org.springframework.messaging.handler.annotation.DestinationVariable; @@ -96,7 +95,7 @@ public ParticipationTeamWebsocketService(WebsocketMessagingService websocketMess /** * Initialize relevant data from hazelcast */ - @PostConstruct + @EventListener(ApplicationReadyEvent.class) public void init() { // participationId-username -> timestamp this.lastTypingTracker = hazelcastInstance.getMap("lastTypingTracker"); @@ -307,11 +306,16 @@ public void handleDisconnect(SessionDisconnectEvent event) { * @param sessionId id of the sessions which is unsubscribing */ public void unsubscribe(String sessionId) { - Optional.ofNullable(destinationTracker.get(sessionId)).ifPresent(destination -> { - Long participationId = getParticipationIdFromDestination(destination); - sendOnlineTeamStudents(participationId, sessionId); - destinationTracker.remove(sessionId); - }); + if (hazelcastInstance.getLifecycleService().isRunning()) { + Optional.ofNullable(destinationTracker.get(sessionId)).ifPresent(destination -> { + Long participationId = getParticipationIdFromDestination(destination); + sendOnlineTeamStudents(participationId, sessionId); + // check if Hazelcast is still active, before invoking this + if (hazelcastInstance.getLifecycleService().isRunning()) { + destinationTracker.remove(sessionId); + } + }); + } } /** diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/localci/LocalCIResultProcessingService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/localci/LocalCIResultProcessingService.java index 39bfac28ca0b..e767b65e9f92 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/localci/LocalCIResultProcessingService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/localci/LocalCIResultProcessingService.java @@ -7,13 +7,14 @@ import java.util.UUID; import java.util.concurrent.CancellationException; -import jakarta.annotation.PostConstruct; import jakarta.annotation.PreDestroy; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.context.annotation.Profile; +import org.springframework.context.event.EventListener; import org.springframework.stereotype.Service; import com.hazelcast.collection.IQueue; @@ -90,7 +91,7 @@ public LocalCIResultProcessingService(@Qualifier("hazelcastInstance") HazelcastI /** * Initializes the result queue, build agent information map and the locks. */ - @PostConstruct + @EventListener(ApplicationReadyEvent.class) public void init() { this.resultQueue = this.hazelcastInstance.getQueue("buildResultQueue"); this.buildAgentInformation = this.hazelcastInstance.getMap("buildAgentInformation"); @@ -99,7 +100,9 @@ public void init() { @PreDestroy public void removeListener() { - this.resultQueue.removeItemListener(this.listenerId); + if (hazelcastInstance.getLifecycleService().isRunning()) { + this.resultQueue.removeItemListener(this.listenerId); + } } /** From ad5c9894b21571bc61407e73fcfa3fa82515692f Mon Sep 17 00:00:00 2001 From: Stephan Krusche Date: Sun, 6 Oct 2024 11:21:52 +0200 Subject: [PATCH 2/4] small code improvements --- .../buildagent/service/SharedQueueProcessingService.java | 3 ++- .../exercise/web/ParticipationTeamWebsocketService.java | 8 +++----- .../service/localci/LocalCIResultProcessingService.java | 3 ++- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/java/de/tum/cit/aet/artemis/buildagent/service/SharedQueueProcessingService.java b/src/main/java/de/tum/cit/aet/artemis/buildagent/service/SharedQueueProcessingService.java index cdb26e9b190e..c88389be1eb7 100644 --- a/src/main/java/de/tum/cit/aet/artemis/buildagent/service/SharedQueueProcessingService.java +++ b/src/main/java/de/tum/cit/aet/artemis/buildagent/service/SharedQueueProcessingService.java @@ -106,7 +106,8 @@ public void init() { @PreDestroy public void removeListener() { - if (hazelcastInstance.getLifecycleService().isRunning()) { + // check if Hazelcast is still active, before invoking this + if (hazelcastInstance != null && hazelcastInstance.getLifecycleService().isRunning()) { this.queue.removeItemListener(this.listenerId); } } diff --git a/src/main/java/de/tum/cit/aet/artemis/exercise/web/ParticipationTeamWebsocketService.java b/src/main/java/de/tum/cit/aet/artemis/exercise/web/ParticipationTeamWebsocketService.java index 8f8d0138e3ab..43fc216860c3 100644 --- a/src/main/java/de/tum/cit/aet/artemis/exercise/web/ParticipationTeamWebsocketService.java +++ b/src/main/java/de/tum/cit/aet/artemis/exercise/web/ParticipationTeamWebsocketService.java @@ -306,14 +306,12 @@ public void handleDisconnect(SessionDisconnectEvent event) { * @param sessionId id of the sessions which is unsubscribing */ public void unsubscribe(String sessionId) { - if (hazelcastInstance.getLifecycleService().isRunning()) { + // check if Hazelcast is still active, before invoking this + if (hazelcastInstance != null && hazelcastInstance.getLifecycleService().isRunning()) { Optional.ofNullable(destinationTracker.get(sessionId)).ifPresent(destination -> { + destinationTracker.remove(sessionId); Long participationId = getParticipationIdFromDestination(destination); sendOnlineTeamStudents(participationId, sessionId); - // check if Hazelcast is still active, before invoking this - if (hazelcastInstance.getLifecycleService().isRunning()) { - destinationTracker.remove(sessionId); - } }); } } diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/localci/LocalCIResultProcessingService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/localci/LocalCIResultProcessingService.java index b2a99eecccf1..9b4e483a1b42 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/localci/LocalCIResultProcessingService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/localci/LocalCIResultProcessingService.java @@ -100,7 +100,8 @@ public void init() { @PreDestroy public void removeListener() { - if (hazelcastInstance.getLifecycleService().isRunning()) { + // check if Hazelcast is still active, before invoking this + if (hazelcastInstance != null && hazelcastInstance.getLifecycleService().isRunning()) { this.resultQueue.removeItemListener(this.listenerId); } } From cf07e1ca85cb1cc851130599e4591905bc9d6b9e Mon Sep 17 00:00:00 2001 From: entholzer Date: Sat, 12 Oct 2024 22:12:41 +0200 Subject: [PATCH 3/4] added try catch around hazelcastInstance.getLifecycleService().isRunning() checks --- .../service/SharedQueueProcessingService.java | 10 ++++++++-- .../web/ParticipationTeamWebsocketService.java | 18 ++++++++++++------ .../LocalCIResultProcessingService.java | 10 ++++++++-- 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/src/main/java/de/tum/cit/aet/artemis/buildagent/service/SharedQueueProcessingService.java b/src/main/java/de/tum/cit/aet/artemis/buildagent/service/SharedQueueProcessingService.java index c88389be1eb7..2575e58181f1 100644 --- a/src/main/java/de/tum/cit/aet/artemis/buildagent/service/SharedQueueProcessingService.java +++ b/src/main/java/de/tum/cit/aet/artemis/buildagent/service/SharedQueueProcessingService.java @@ -33,6 +33,7 @@ import com.hazelcast.collection.ItemEvent; import com.hazelcast.collection.ItemListener; import com.hazelcast.core.HazelcastInstance; +import com.hazelcast.core.HazelcastInstanceNotActiveException; import com.hazelcast.map.IMap; import de.tum.cit.aet.artemis.buildagent.dto.BuildAgentInformation; @@ -107,8 +108,13 @@ public void init() { @PreDestroy public void removeListener() { // check if Hazelcast is still active, before invoking this - if (hazelcastInstance != null && hazelcastInstance.getLifecycleService().isRunning()) { - this.queue.removeItemListener(this.listenerId); + try { + if (hazelcastInstance != null && hazelcastInstance.getLifecycleService().isRunning()) { + this.queue.removeItemListener(this.listenerId); + } + } + catch (HazelcastInstanceNotActiveException e) { + log.error("Failed to remove listener from SharedQueueProcessingService as Hazelcast instance is not active any more."); } } diff --git a/src/main/java/de/tum/cit/aet/artemis/exercise/web/ParticipationTeamWebsocketService.java b/src/main/java/de/tum/cit/aet/artemis/exercise/web/ParticipationTeamWebsocketService.java index 43fc216860c3..ad7d9b0fa16a 100644 --- a/src/main/java/de/tum/cit/aet/artemis/exercise/web/ParticipationTeamWebsocketService.java +++ b/src/main/java/de/tum/cit/aet/artemis/exercise/web/ParticipationTeamWebsocketService.java @@ -30,6 +30,7 @@ import org.springframework.web.socket.messaging.SessionUnsubscribeEvent; import com.hazelcast.core.HazelcastInstance; +import com.hazelcast.core.HazelcastInstanceNotActiveException; import de.tum.cit.aet.artemis.communication.service.WebsocketMessagingService; import de.tum.cit.aet.artemis.core.domain.User; @@ -307,12 +308,17 @@ public void handleDisconnect(SessionDisconnectEvent event) { */ public void unsubscribe(String sessionId) { // check if Hazelcast is still active, before invoking this - if (hazelcastInstance != null && hazelcastInstance.getLifecycleService().isRunning()) { - Optional.ofNullable(destinationTracker.get(sessionId)).ifPresent(destination -> { - destinationTracker.remove(sessionId); - Long participationId = getParticipationIdFromDestination(destination); - sendOnlineTeamStudents(participationId, sessionId); - }); + try { + if (hazelcastInstance != null && hazelcastInstance.getLifecycleService().isRunning()) { + Optional.ofNullable(destinationTracker.get(sessionId)).ifPresent(destination -> { + destinationTracker.remove(sessionId); + Long participationId = getParticipationIdFromDestination(destination); + sendOnlineTeamStudents(participationId, sessionId); + }); + } + } + catch (HazelcastInstanceNotActiveException e) { + log.error("Failed to unsubscribe as Hazelcast is no longer active"); } } diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/localci/LocalCIResultProcessingService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/localci/LocalCIResultProcessingService.java index 9b4e483a1b42..2aaee655e6b3 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/localci/LocalCIResultProcessingService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/localci/LocalCIResultProcessingService.java @@ -21,6 +21,7 @@ import com.hazelcast.collection.ItemEvent; import com.hazelcast.collection.ItemListener; import com.hazelcast.core.HazelcastInstance; +import com.hazelcast.core.HazelcastInstanceNotActiveException; import com.hazelcast.map.IMap; import de.tum.cit.aet.artemis.assessment.domain.Result; @@ -101,8 +102,13 @@ public void init() { @PreDestroy public void removeListener() { // check if Hazelcast is still active, before invoking this - if (hazelcastInstance != null && hazelcastInstance.getLifecycleService().isRunning()) { - this.resultQueue.removeItemListener(this.listenerId); + try { + if (hazelcastInstance != null && hazelcastInstance.getLifecycleService().isRunning()) { + this.resultQueue.removeItemListener(this.listenerId); + } + } + catch (HazelcastInstanceNotActiveException e) { + log.error("Could not remove listener as hazelcast instance is not active."); } } From d2602f9e81be4ea706e9f5b54e8b5060c6822807 Mon Sep 17 00:00:00 2001 From: entholzer Date: Sat, 19 Oct 2024 14:46:14 +0200 Subject: [PATCH 4/4] added java docs to pass style check --- .../service/localci/LocalCIResultProcessingService.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/localci/LocalCIResultProcessingService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/localci/LocalCIResultProcessingService.java index 2aaee655e6b3..a450fc545886 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/localci/LocalCIResultProcessingService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/localci/LocalCIResultProcessingService.java @@ -99,6 +99,10 @@ public void init() { this.listenerId = resultQueue.addItemListener(new ResultQueueListener(), true); } + /** + * Removes the item listener from the Hazelcast result queue if the instance is active. + * Logs an error if Hazelcast is not running. + */ @PreDestroy public void removeListener() { // check if Hazelcast is still active, before invoking this