Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Development: Fix hazelcast issue on server shutdown #9602

Merged
merged 3 commits into from
Oct 27, 2024
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,28 @@

import java.util.List;
import java.util.Map;
import java.util.Optional;

import jakarta.annotation.PostConstruct;

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.stereotype.Service;

import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.core.HazelcastInstanceNotActiveException;

import de.tum.cit.aet.artemis.communication.service.WebsocketMessagingService;

@Profile(PROFILE_CORE)
@Service
public class FeatureToggleService {

private static final Logger log = LoggerFactory.getLogger(FeatureToggleService.class);

private static final String TOPIC_FEATURE_TOGGLES = "/topic/management/feature-toggles";

@Value("${artemis.science.event-logging.enable:false}")
Expand All @@ -36,10 +42,22 @@ public FeatureToggleService(WebsocketMessagingService websocketMessagingService,
this.hazelcastInstance = hazelcastInstance;
}

private Optional<Map<Feature, Boolean>> getFeatures() {
try {
if (hazelcastInstance != null && hazelcastInstance.getLifecycleService().isRunning()) {
return Optional.ofNullable(features);
}
}
catch (HazelcastInstanceNotActiveException e) {
log.error("Failed to get features in FeatureToggleService as Hazelcast instance is not active any more.");
BBesrour marked this conversation as resolved.
Show resolved Hide resolved
}
return Optional.empty();
}
BBesrour marked this conversation as resolved.
Show resolved Hide resolved

/**
* Initialize relevant data from hazelcast
*/
@PostConstruct
@EventListener(ApplicationReadyEvent.class)
public void init() {
// The map will automatically be distributed between all instances by Hazelcast.
features = hazelcastInstance.getMap("features");
Expand All @@ -63,8 +81,10 @@ public void init() {
* @param feature The feature that should be enabled
*/
public void enableFeature(Feature feature) {
features.put(feature, true);
sendUpdate();
getFeatures().ifPresent(features -> {
features.put(feature, true);
sendUpdate();
});
}

/**
Expand All @@ -73,23 +93,34 @@ public void enableFeature(Feature feature) {
* @param feature The feature that should be disabled
*/
public void disableFeature(Feature feature) {
features.put(feature, false);
sendUpdate();
getFeatures().ifPresent(features -> {
features.put(feature, false);
sendUpdate();
});
}

/**
* Updates the given feature toggles and enables/disables the features based on the given map. Also notifies all clients
* by sending a message via the websocket.
*
* @param features A map of features (feature -> shouldBeActivated)
* @param updatedFeatures A map of features (feature -> shouldBeActivated)
*/
public void updateFeatureToggles(final Map<Feature, Boolean> features) {
this.features.putAll(features);
sendUpdate();
public void updateFeatureToggles(final Map<Feature, Boolean> updatedFeatures) {
getFeatures().ifPresent(features -> {
features.putAll(updatedFeatures);
sendUpdate();
});
}

private void sendUpdate() {
websocketMessagingService.sendMessage(TOPIC_FEATURE_TOGGLES, enabledFeatures());
try {
if (hazelcastInstance != null && hazelcastInstance.getLifecycleService().isRunning()) {
BBesrour marked this conversation as resolved.
Show resolved Hide resolved
websocketMessagingService.sendMessage(TOPIC_FEATURE_TOGGLES, enabledFeatures());
}
}
catch (HazelcastInstanceNotActiveException e) {
log.error("Failed to send features update in FeatureToggleService as Hazelcast instance is not active any more.");
BBesrour marked this conversation as resolved.
Show resolved Hide resolved
}
}
BBesrour marked this conversation as resolved.
Show resolved Hide resolved

/**
Expand All @@ -99,8 +130,16 @@ private void sendUpdate() {
* @return if the feature is enabled
*/
public boolean isFeatureEnabled(Feature feature) {
Boolean isEnabled = features.get(feature);
return Boolean.TRUE.equals(isEnabled);
try {
if (hazelcastInstance != null && hazelcastInstance.getLifecycleService().isRunning()) {
Boolean isEnabled = features.get(feature);
return Boolean.TRUE.equals(isEnabled);
}
}
catch (HazelcastInstanceNotActiveException e) {
log.error("Failed to check if feature is enabled in FeatureToggleService as Hazelcast instance is not active any more.");
}
return false;
}

/**
Expand All @@ -109,7 +148,15 @@ public boolean isFeatureEnabled(Feature feature) {
* @return A list of enabled features
*/
public List<Feature> enabledFeatures() {
return features.entrySet().stream().filter(feature -> Boolean.TRUE.equals(feature.getValue())).map(Map.Entry::getKey).toList();
try {
if (hazelcastInstance != null && hazelcastInstance.getLifecycleService().isRunning()) {
return features.entrySet().stream().filter(feature -> Boolean.TRUE.equals(feature.getValue())).map(Map.Entry::getKey).toList();
}
}
catch (HazelcastInstanceNotActiveException e) {
log.error("Failed to retrieve enabled features update in FeatureToggleService as Hazelcast instance is not active any more.");
}
return List.of();
}

/**
Expand All @@ -118,6 +165,14 @@ public List<Feature> enabledFeatures() {
* @return A list of disabled features
*/
public List<Feature> disabledFeatures() {
return features.entrySet().stream().filter(feature -> Boolean.FALSE.equals(feature.getValue())).map(Map.Entry::getKey).toList();
try {
if (hazelcastInstance != null && hazelcastInstance.getLifecycleService().isRunning()) {
return features.entrySet().stream().filter(feature -> Boolean.FALSE.equals(feature.getValue())).map(Map.Entry::getKey).toList();
}
}
catch (HazelcastInstanceNotActiveException e) {
log.error("Failed to retrieve disabled features update in FeatureToggleService as Hazelcast instance is not active any more.");
}
return List.of();
}
}
Loading