|
|
|
@ -19,7 +19,12 @@ import com.google.protobuf.ByteString; |
|
|
|
import lombok.RequiredArgsConstructor; |
|
|
|
import lombok.extern.slf4j.Slf4j; |
|
|
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; |
|
|
|
import org.springframework.boot.context.properties.ConfigurationProperties; |
|
|
|
import org.springframework.stereotype.Service; |
|
|
|
import org.springframework.util.ConcurrentReferenceHashMap; |
|
|
|
import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType; |
|
|
|
import org.thingsboard.server.common.data.notification.settings.TriggerTypeConfig; |
|
|
|
import org.thingsboard.server.common.msg.notification.NotificationRuleProcessor; |
|
|
|
import org.thingsboard.server.common.msg.notification.trigger.NotificationRuleTrigger; |
|
|
|
import org.thingsboard.server.common.msg.queue.ServiceType; |
|
|
|
import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; |
|
|
|
@ -30,10 +35,17 @@ import org.thingsboard.server.queue.discovery.PartitionService; |
|
|
|
import org.thingsboard.server.queue.provider.TbQueueProducerProvider; |
|
|
|
import org.thingsboard.server.queue.util.DataDecodingEncodingService; |
|
|
|
|
|
|
|
import java.util.EnumMap; |
|
|
|
import java.util.Map; |
|
|
|
import java.util.UUID; |
|
|
|
import java.util.concurrent.ConcurrentMap; |
|
|
|
import java.util.concurrent.atomic.AtomicBoolean; |
|
|
|
|
|
|
|
import static org.springframework.util.ConcurrentReferenceHashMap.ReferenceType.SOFT; |
|
|
|
|
|
|
|
@Service |
|
|
|
@ConditionalOnMissingBean(value = NotificationRuleProcessor.class, ignored = RemoteNotificationRuleProcessor.class) |
|
|
|
@ConfigurationProperties(prefix = "notification-system.rules") |
|
|
|
@RequiredArgsConstructor |
|
|
|
@Slf4j |
|
|
|
public class RemoteNotificationRuleProcessor implements NotificationRuleProcessor { |
|
|
|
@ -43,10 +55,16 @@ public class RemoteNotificationRuleProcessor implements NotificationRuleProcesso |
|
|
|
private final PartitionService partitionService; |
|
|
|
private final DataDecodingEncodingService encodingService; |
|
|
|
|
|
|
|
private Map<NotificationRuleTriggerType, TriggerTypeConfig> triggerTypesConfigs; |
|
|
|
private final ConcurrentMap<String, Long> submittedTriggers = new ConcurrentReferenceHashMap<>(16, SOFT); |
|
|
|
|
|
|
|
@Override |
|
|
|
public void process(NotificationRuleTrigger trigger) { |
|
|
|
if (trigger.deduplicate() && alreadySubmitted(trigger)) { |
|
|
|
return; |
|
|
|
} |
|
|
|
try { |
|
|
|
log.trace("Submitting notification rule trigger: {}", trigger); |
|
|
|
log.debug("Submitting notification rule trigger: {}", trigger); |
|
|
|
TransportProtos.NotificationRuleProcessorMsg.Builder msg = TransportProtos.NotificationRuleProcessorMsg.newBuilder() |
|
|
|
.setTrigger(ByteString.copyFrom(encodingService.encode(trigger))); |
|
|
|
|
|
|
|
@ -57,9 +75,52 @@ public class RemoteNotificationRuleProcessor implements NotificationRuleProcesso |
|
|
|
.setNotificationRuleProcessorMsg(msg) |
|
|
|
.build()), null); |
|
|
|
}); |
|
|
|
} catch (Exception e) { |
|
|
|
} catch (Throwable e) { |
|
|
|
log.error("Failed to submit notification rule trigger: {}", trigger, e); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private boolean alreadySubmitted(NotificationRuleTrigger trigger) { |
|
|
|
String deduplicationKey = trigger.getDeduplicationKey(); |
|
|
|
|
|
|
|
AtomicBoolean alreadySubmitted = new AtomicBoolean(false); |
|
|
|
submittedTriggers.compute(deduplicationKey, (key, lastSubmittedTs) -> { |
|
|
|
long currentTs = System.currentTimeMillis(); |
|
|
|
if (lastSubmittedTs == null) { |
|
|
|
return currentTs; |
|
|
|
} else { |
|
|
|
long deduplicationDuration = getDeduplicationDuration(trigger); |
|
|
|
long passed = currentTs - lastSubmittedTs; |
|
|
|
if (deduplicationDuration == 0 || passed <= deduplicationDuration) { |
|
|
|
log.trace("Notification rule trigger {} was already submitted {} ms ago, deduplication duration is {} ms. Key: '{}'", |
|
|
|
trigger.getType(), passed, deduplicationDuration, deduplicationKey); |
|
|
|
alreadySubmitted.set(true); |
|
|
|
return lastSubmittedTs; |
|
|
|
} else { |
|
|
|
return currentTs; |
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
return alreadySubmitted.get(); |
|
|
|
} |
|
|
|
|
|
|
|
private long getDeduplicationDuration(NotificationRuleTrigger trigger) { |
|
|
|
if (triggerTypesConfigs == null) { |
|
|
|
triggerTypesConfigs = new EnumMap<>(NotificationRuleTriggerType.class); |
|
|
|
} |
|
|
|
TriggerTypeConfig triggerTypeConfig = triggerTypesConfigs.computeIfAbsent(trigger.getType(), triggerType -> { |
|
|
|
TriggerTypeConfig config = new TriggerTypeConfig(); |
|
|
|
config.setDeduplicationDuration(trigger.getDefaultDeduplicationDuration()); |
|
|
|
return config; |
|
|
|
}); |
|
|
|
return triggerTypeConfig.getDeduplicationDuration(); |
|
|
|
} |
|
|
|
|
|
|
|
// set from ConfigurationProperties
|
|
|
|
public void setTriggerTypesConfigs(Map<NotificationRuleTriggerType, TriggerTypeConfig> triggerTypesConfigs) { |
|
|
|
if (triggerTypesConfigs != null) { |
|
|
|
this.triggerTypesConfigs = new EnumMap<>(triggerTypesConfigs); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|