From 056068377886c2c748e84a7d9059e211fd0da4c5 Mon Sep 17 00:00:00 2001 From: Stephan Krusche Date: Sun, 20 Oct 2024 11:41:51 +0200 Subject: [PATCH] Development: Improve boundary cases with Hazelcast (#9387) (cherry picked from commit f3a48adb43f270f73a20f6e76b080aacc380da06) --- .../service/SharedQueueProcessingService.java | 18 ++++++++++---- .../ParticipationTeamWebsocketService.java | 24 ++++++++++++------- .../LocalCIResultProcessingService.java | 20 +++++++++++++--- 3 files changed, 47 insertions(+), 15 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 69402e346a1f..e73f8bac244b 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 @@ -23,14 +23,15 @@ 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.beans.factory.annotation.Value; +import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.context.annotation.Profile; +import org.springframework.context.event.EventListener; import org.springframework.scheduling.TaskScheduler; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; @@ -40,6 +41,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 com.hazelcast.topic.ITopic; @@ -130,7 +132,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"); @@ -172,7 +174,15 @@ public void removeListenerAndCancelScheduledFuture() { } private void removeListener() { - this.queue.removeItemListener(this.listenerId); + // check if Hazelcast is still active, before invoking this + 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."); + } } private void cancelCheckAvailabilityAndProcessNextBuildScheduledFuture() { @@ -205,7 +215,7 @@ public void updateBuildAgentInformation() { * 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..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 @@ -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; @@ -31,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; @@ -96,7 +96,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 +307,19 @@ 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); - }); + // check if Hazelcast is still active, before invoking this + 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 4507f1a4e76d..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 @@ -7,19 +7,21 @@ 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; 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; @@ -90,16 +92,28 @@ 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"); 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() { - this.resultQueue.removeItemListener(this.listenerId); + // check if Hazelcast is still active, before invoking this + 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."); + } } /**