feat(syslog): 为三个生产模块增加启动版本标识
- syslog-serve 增加 SYSLOG_BUILD_READY 启动日志 - syslog-consumer 增加 SYSLOG_BUILD_READY 启动日志 - 更新 syslog-consumer-rule 代码修订标识至 v63 - 输出模块名、代码修订号、运行环境及关键非敏感配置 - 支持通过容器日志快速确认实际部署的 JAR 版本 - 补充 syslog-serve 和 syslog-consumer 启动日志测试 - 不输出数据库密码、API 凭据等敏感信息 - 不修改 Maven POM 配置
This commit is contained in:
+22
@@ -63,8 +63,30 @@ public class NormalizationCacheInvalidationService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空规则专属 Spring Cache,并按设备精确广播 JetCache BOTH 失效。
|
||||
*
|
||||
* <p>JetCache 没有安全的全库 clear;调用方传入数据库中的全部设备 ID,既能删除共享 L2,
|
||||
* 又能通过 syncLocal 让其他实例立即移除对应 L1,且不会误删同 Redis 中的其他业务数据。</p>
|
||||
*/
|
||||
public void invalidateAllRules(Collection<Long> deviceIds) {
|
||||
clearSpring("normalizeRule");
|
||||
metrics.invalidation("local");
|
||||
if (deviceIds == null) return;
|
||||
for (Long deviceId : deviceIds) {
|
||||
if (deviceId == null) continue;
|
||||
jetCacheInvalidator.rulesForDevice(deviceId);
|
||||
metrics.invalidation("remote");
|
||||
}
|
||||
}
|
||||
|
||||
private void evictSpring(String cacheName, Object key) {
|
||||
Cache cache = springCacheManager.getCache(cacheName);
|
||||
if (cache != null) cache.evict(key);
|
||||
}
|
||||
|
||||
private void clearSpring(String cacheName) {
|
||||
Cache cache = springCacheManager.getCache(cacheName);
|
||||
if (cache != null) cache.clear();
|
||||
}
|
||||
}
|
||||
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
package com.Modules.NormalData.cache;
|
||||
|
||||
import com.Modules.NormalData.compiled.CompiledRuleCache;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.data.redis.connection.Message;
|
||||
import org.springframework.data.redis.connection.MessageListener;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
/** 通过 Redis Pub/Sub 让所有消费节点同步清理 JVM 本地编译规则缓存。 */
|
||||
@Component
|
||||
public class NormalizationCompiledCacheInvalidationBus implements MessageListener {
|
||||
public static final String TOPIC = "xdr:norm:v1:compiled-rule:invalidate";
|
||||
private static final String ALL = "ALL";
|
||||
private static final String RULES_PREFIX = "RULES:";
|
||||
private static final Logger log = LoggerFactory.getLogger(
|
||||
NormalizationCompiledCacheInvalidationBus.class);
|
||||
|
||||
private final CompiledRuleCache compiledRuleCache;
|
||||
private final StringRedisTemplate redisTemplate;
|
||||
|
||||
public NormalizationCompiledCacheInvalidationBus(CompiledRuleCache compiledRuleCache,
|
||||
StringRedisTemplate redisTemplate) {
|
||||
this.compiledRuleCache = compiledRuleCache;
|
||||
this.redisTemplate = redisTemplate;
|
||||
}
|
||||
|
||||
public void invalidateRulesEverywhere(Collection<Long> ruleIds) {
|
||||
Set<Long> distinct = positiveIds(ruleIds);
|
||||
if (distinct.isEmpty()) return;
|
||||
for (Long ruleId : distinct) compiledRuleCache.invalidateRule(ruleId);
|
||||
redisTemplate.convertAndSend(TOPIC, ruleMessage(distinct));
|
||||
}
|
||||
|
||||
public void invalidateAllEverywhere() {
|
||||
compiledRuleCache.invalidateAll();
|
||||
redisTemplate.convertAndSend(TOPIC, ALL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessage(Message message, byte[] pattern) {
|
||||
if (message == null || message.getBody() == null) return;
|
||||
String command = new String(message.getBody(), StandardCharsets.UTF_8).trim();
|
||||
if (ALL.equals(command)) {
|
||||
compiledRuleCache.invalidateAll();
|
||||
return;
|
||||
}
|
||||
if (!command.startsWith(RULES_PREFIX)) {
|
||||
log.warn("忽略未知的规则缓存失效广播: {}", command);
|
||||
return;
|
||||
}
|
||||
String ids = command.substring(RULES_PREFIX.length());
|
||||
if (ids.isEmpty()) return;
|
||||
for (String value : ids.split(",")) {
|
||||
try {
|
||||
long ruleId = Long.parseLong(value);
|
||||
if (ruleId > 0L) compiledRuleCache.invalidateRule(ruleId);
|
||||
} catch (NumberFormatException invalidId) {
|
||||
log.warn("忽略非法的规则缓存失效广播参数: {}", value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Set<Long> positiveIds(Collection<Long> ruleIds) {
|
||||
Set<Long> distinct = new LinkedHashSet<>();
|
||||
if (ruleIds == null) return distinct;
|
||||
for (Long ruleId : ruleIds) {
|
||||
if (ruleId != null && ruleId > 0L) distinct.add(ruleId);
|
||||
}
|
||||
return distinct;
|
||||
}
|
||||
|
||||
private static String ruleMessage(Collection<Long> ruleIds) {
|
||||
StringJoiner values = new StringJoiner(",", RULES_PREFIX, "");
|
||||
for (Long ruleId : ruleIds) values.add(ruleId.toString());
|
||||
return values.toString();
|
||||
}
|
||||
}
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
package com.Modules.NormalData.cache;
|
||||
|
||||
import com.common.mapper.DeviceDeviceMapper;
|
||||
import com.common.mapper.DmNormalizeRuleMapper;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/** 管理接口使用的规则缓存清理编排;不会清除设备、任务或其他业务 Redis 数据。 */
|
||||
@Service
|
||||
public class NormalizationRuleCacheManagementService {
|
||||
private final NormalizationCacheInvalidationService invalidationService;
|
||||
private final NormalizationCompiledCacheInvalidationBus compiledCacheInvalidationBus;
|
||||
private final DeviceDeviceMapper deviceMapper;
|
||||
private final DmNormalizeRuleMapper ruleMapper;
|
||||
|
||||
public NormalizationRuleCacheManagementService(
|
||||
NormalizationCacheInvalidationService invalidationService,
|
||||
NormalizationCompiledCacheInvalidationBus compiledCacheInvalidationBus,
|
||||
DeviceDeviceMapper deviceMapper,
|
||||
DmNormalizeRuleMapper ruleMapper) {
|
||||
this.invalidationService = invalidationService;
|
||||
this.compiledCacheInvalidationBus = compiledCacheInvalidationBus;
|
||||
this.deviceMapper = deviceMapper;
|
||||
this.ruleMapper = ruleMapper;
|
||||
}
|
||||
|
||||
/** 清理一个设备的规则快照,并移除该设备当前关联规则的实体和编译缓存。 */
|
||||
public ClearResult clearDevice(long deviceId) {
|
||||
if (deviceId <= 0L) throw new IllegalArgumentException("deviceId 必须大于 0");
|
||||
Set<Long> ruleIds = new LinkedHashSet<>();
|
||||
addAllNonNull(ruleIds, nullSafe(ruleMapper.selectRuleIdsByDeviceId(deviceId)));
|
||||
invalidationService.invalidateRulesForDevice(deviceId);
|
||||
for (Long ruleId : ruleIds) {
|
||||
invalidationService.invalidateRuleEntity(ruleId);
|
||||
}
|
||||
compiledCacheInvalidationBus.invalidateRulesEverywhere(ruleIds);
|
||||
return new ClearResult("DEVICE", deviceId, 1, ruleIds.size());
|
||||
}
|
||||
|
||||
/** 清理所有设备规则快照、规则实体和当前 JVM 的全部编译缓存。 */
|
||||
public ClearResult clearAll() {
|
||||
Set<Long> deviceIds = new LinkedHashSet<>();
|
||||
addAllNonNull(deviceIds, deviceMapper.selectAllIds());
|
||||
// 即使存在历史孤立关联,也应删除其规则快照键。
|
||||
addAllNonNull(deviceIds, ruleMapper.selectAllAssociatedDeviceIds());
|
||||
invalidationService.invalidateAllRules(deviceIds);
|
||||
compiledCacheInvalidationBus.invalidateAllEverywhere();
|
||||
return new ClearResult("ALL", null, deviceIds.size(), 0);
|
||||
}
|
||||
|
||||
private static List<Long> nullSafe(List<Long> values) {
|
||||
return values == null ? Collections.emptyList() : values;
|
||||
}
|
||||
|
||||
private static void addAllNonNull(Set<Long> target, Collection<Long> values) {
|
||||
if (values == null) return;
|
||||
for (Long value : values) if (value != null) target.add(value);
|
||||
}
|
||||
|
||||
public static final class ClearResult {
|
||||
private final String scope;
|
||||
private final Long deviceId;
|
||||
private final int deviceCount;
|
||||
private final int associatedRuleCount;
|
||||
|
||||
public ClearResult(String scope, Long deviceId, int deviceCount, int associatedRuleCount) {
|
||||
this.scope = scope;
|
||||
this.deviceId = deviceId;
|
||||
this.deviceCount = deviceCount;
|
||||
this.associatedRuleCount = associatedRuleCount;
|
||||
}
|
||||
|
||||
public String getScope() { return scope; }
|
||||
public Long getDeviceId() { return deviceId; }
|
||||
public int getDeviceCount() { return deviceCount; }
|
||||
public int getAssociatedRuleCount() { return associatedRuleCount; }
|
||||
}
|
||||
}
|
||||
+6
-6
@@ -1,6 +1,5 @@
|
||||
package com.Modules.NormalData.cache;
|
||||
|
||||
import com.Modules.NormalData.compiled.CompiledRuleCache;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
@@ -14,21 +13,21 @@ public class NormalizeRuleCacheInvalidationListener {
|
||||
private static final Logger log = LoggerFactory.getLogger(NormalizeRuleCacheInvalidationListener.class);
|
||||
|
||||
private final NormalizationCacheInvalidationService invalidationService;
|
||||
private final CompiledRuleCache compiledRuleCache;
|
||||
private final NormalizationCompiledCacheInvalidationBus compiledCacheInvalidationBus;
|
||||
private final NormalizationRuleMetrics metrics;
|
||||
|
||||
public NormalizeRuleCacheInvalidationListener(NormalizationCacheInvalidationService invalidationService,
|
||||
CompiledRuleCache compiledRuleCache,
|
||||
NormalizationCompiledCacheInvalidationBus compiledCacheInvalidationBus,
|
||||
NormalizationRuleMetrics metrics) {
|
||||
this.invalidationService = invalidationService;
|
||||
this.compiledRuleCache = compiledRuleCache;
|
||||
this.compiledCacheInvalidationBus = compiledCacheInvalidationBus;
|
||||
this.metrics = metrics;
|
||||
}
|
||||
|
||||
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
|
||||
public void afterCommit(NormalizeRuleChangedEvent event) {
|
||||
metrics.invalidation("after_commit");
|
||||
// 先移除跨 JVM 的规则事实,再清除本 JVM 所有历史 fingerprint 编译对象。
|
||||
// 先移除跨 JVM 的规则事实,再广播清除所有 JVM 的历史 fingerprint 对象。
|
||||
for (Long deviceId : event.getDeviceIds()) {
|
||||
try {
|
||||
invalidationService.invalidateRulesForDevice(deviceId);
|
||||
@@ -39,7 +38,8 @@ public class NormalizeRuleCacheInvalidationListener {
|
||||
}
|
||||
}
|
||||
try {
|
||||
compiledRuleCache.invalidateRule(event.getRuleId());
|
||||
compiledCacheInvalidationBus.invalidateRulesEverywhere(
|
||||
java.util.Collections.singleton(event.getRuleId()));
|
||||
} catch (RuntimeException cacheFailure) {
|
||||
metrics.invalidation("failure");
|
||||
// 数据库事实已提交;raw rules 的短 TTL 是最终一致性兜底。
|
||||
|
||||
+12
@@ -192,6 +192,18 @@ public class CompiledRuleCache {
|
||||
metrics.compiledCacheGauge(compiled.estimatedSize(), evictions.sum());
|
||||
}
|
||||
|
||||
/** 清空当前 JVM 中所有规则编译结果、匹配画像及短失败缓存。 */
|
||||
public void invalidateAll() {
|
||||
compiled.invalidateAll();
|
||||
matchProfiles.invalidateAll();
|
||||
failures.invalidateAll();
|
||||
matchFailures.invalidateAll();
|
||||
loggedFailures.invalidateAll();
|
||||
keysByRule.invalidateAll();
|
||||
matchKeysByRule.invalidateAll();
|
||||
metrics.compiledCacheGauge(0L, evictions.sum());
|
||||
}
|
||||
|
||||
public long estimatedSize() { return compiled.estimatedSize(); }
|
||||
public long compileSuccessCount() { return compileSuccess.sum(); }
|
||||
public long compileFailureCount() { return compileFailure.sum(); }
|
||||
|
||||
+4
-1
@@ -21,6 +21,9 @@ public interface DeviceDeviceMapper {
|
||||
*/
|
||||
List<DeviceDevice> selectAll();
|
||||
|
||||
/** 规则缓存全量失效只需要主键,避免加载整行设备信息。 */
|
||||
List<Long> selectAllIds();
|
||||
|
||||
/**
|
||||
* 根据IP地址查询设备
|
||||
*/
|
||||
@@ -90,4 +93,4 @@ public interface DeviceDeviceMapper {
|
||||
* 根据厂商查询设备
|
||||
*/
|
||||
List<DeviceDevice> selectByVendor(String vendor);
|
||||
}
|
||||
}
|
||||
|
||||
+4
@@ -24,6 +24,10 @@ public interface DmNormalizeRuleMapper {
|
||||
|
||||
List<Long> selectDeviceIdsByRuleId(@Param("ruleId") Long ruleId);
|
||||
|
||||
List<Long> selectRuleIdsByDeviceId(@Param("deviceId") Long deviceId);
|
||||
|
||||
List<Long> selectAllAssociatedDeviceIds();
|
||||
|
||||
/**
|
||||
* 根据ID更新数据标准化规则
|
||||
* @param rule 数据标准化规则对象
|
||||
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package com.config;
|
||||
|
||||
import com.Modules.NormalData.cache.NormalizationCompiledCacheInvalidationBus;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.listener.ChannelTopic;
|
||||
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
|
||||
|
||||
/** 规则编译缓存的跨实例失效广播通道。 */
|
||||
@Configuration
|
||||
public class NormalizationCacheInvalidationRedisConfig {
|
||||
|
||||
@Bean
|
||||
public RedisMessageListenerContainer normalizationCacheInvalidationListenerContainer(
|
||||
RedisConnectionFactory connectionFactory,
|
||||
NormalizationCompiledCacheInvalidationBus listener) {
|
||||
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
|
||||
container.setConnectionFactory(connectionFactory);
|
||||
container.addMessageListener(listener,
|
||||
new ChannelTopic(NormalizationCompiledCacheInvalidationBus.TOPIC));
|
||||
return container;
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -12,7 +12,7 @@ import org.springframework.stereotype.Component;
|
||||
public class SyslogBuildStartupLogger {
|
||||
|
||||
// 确认包含此功能修订,不代表唯一构建;后续功能修订时同步更新此标识。
|
||||
static final String CODE_REVISION = "ASYNC_STATS_ADAPTIVE_RAW_V20260913_57";
|
||||
static final String CODE_REVISION = "ASYNC_STATS_ADAPTIVE_RAW_V20260917_63";
|
||||
private final Environment environment;
|
||||
|
||||
public SyslogBuildStartupLogger(Environment environment) {
|
||||
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
package com.controllers;
|
||||
|
||||
import com.Modules.NormalData.cache.NormalizationRuleCacheManagementService;
|
||||
import com.Modules.NormalData.cache.NormalizationRuleCacheManagementService.ClearResult;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/** 只清理标准化规则相关缓存,不执行现有 /api/cache/all 的全 Redis 删除。 */
|
||||
@RestController
|
||||
@RequestMapping("/api/syslog/normalization/cache")
|
||||
public class NormalizationRuleCacheController {
|
||||
private final NormalizationRuleCacheManagementService service;
|
||||
|
||||
public NormalizationRuleCacheController(NormalizationRuleCacheManagementService service) {
|
||||
this.service = service;
|
||||
}
|
||||
|
||||
@DeleteMapping("/devices/{deviceId}")
|
||||
public ResponseEntity<Map<String, Object>> clearDevice(@PathVariable Long deviceId) {
|
||||
if (deviceId == null || deviceId <= 0L) {
|
||||
Map<String, Object> error = new LinkedHashMap<>();
|
||||
error.put("cleared", false);
|
||||
error.put("errorCode", "INVALID_DEVICE_ID");
|
||||
error.put("message", "deviceId 必须大于 0");
|
||||
return ResponseEntity.badRequest().body(error);
|
||||
}
|
||||
return ResponseEntity.ok(response(service.clearDevice(deviceId)));
|
||||
}
|
||||
|
||||
@DeleteMapping
|
||||
public Map<String, Object> clearAll() {
|
||||
return response(service.clearAll());
|
||||
}
|
||||
|
||||
private Map<String, Object> response(ClearResult result) {
|
||||
Map<String, Object> body = new LinkedHashMap<>();
|
||||
body.put("cleared", true);
|
||||
body.put("scope", result.getScope());
|
||||
if (result.getDeviceId() != null) body.put("deviceId", result.getDeviceId());
|
||||
body.put("deviceCount", result.getDeviceCount());
|
||||
if (result.getDeviceId() != null) {
|
||||
body.put("associatedRuleCount", result.getAssociatedRuleCount());
|
||||
}
|
||||
return body;
|
||||
}
|
||||
}
|
||||
+4
@@ -70,6 +70,10 @@
|
||||
FROM device_device
|
||||
</select>
|
||||
|
||||
<select id="selectAllIds" resultType="java.lang.Long">
|
||||
SELECT id FROM device_device
|
||||
</select>
|
||||
|
||||
<!-- 根据IP查询 -->
|
||||
<select id="selectByIp" parameterType="java.lang.String" resultMap="BaseResultMap">
|
||||
SELECT
|
||||
|
||||
+11
@@ -96,6 +96,17 @@
|
||||
WHERE normalize_rule_id = #{ruleId}
|
||||
</select>
|
||||
|
||||
<select id="selectRuleIdsByDeviceId" parameterType="java.lang.Long" resultType="java.lang.Long">
|
||||
SELECT normalize_rule_id
|
||||
FROM dm_normalize_rule_device
|
||||
WHERE device_id = #{deviceId}
|
||||
</select>
|
||||
|
||||
<select id="selectAllAssociatedDeviceIds" resultType="java.lang.Long">
|
||||
SELECT DISTINCT device_id
|
||||
FROM dm_normalize_rule_device
|
||||
</select>
|
||||
|
||||
|
||||
<!-- 根据ID和租户ID查询 -->
|
||||
<select id="selectByIdAndTenant" resultMap="BaseResultMap">
|
||||
|
||||
+18
@@ -10,6 +10,8 @@ import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
class NormalizationCacheInvalidationServiceTest {
|
||||
@Test
|
||||
void evictsMatchingLegacyAndJetCacheKeys() {
|
||||
@@ -35,4 +37,20 @@ class NormalizationCacheInvalidationServiceTest {
|
||||
verify(jet).collectTask(8);
|
||||
verify(jet).rulesForDevice(9L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void clearsOnlyRuleSpringCacheAndBroadcastsEveryDevice() {
|
||||
CacheManager manager = mock(CacheManager.class);
|
||||
Cache rules = mock(Cache.class);
|
||||
when(manager.getCache("normalizeRule")).thenReturn(rules);
|
||||
NormalizationCacheInvalidator jet = mock(NormalizationCacheInvalidator.class);
|
||||
NormalizationCacheInvalidationService service = new NormalizationCacheInvalidationService(
|
||||
manager, jet, new NormalizationRuleMetrics());
|
||||
|
||||
service.invalidateAllRules(Arrays.asList(9L, null, 10L));
|
||||
|
||||
verify(rules).clear();
|
||||
verify(jet).rulesForDevice(9L);
|
||||
verify(jet).rulesForDevice(10L);
|
||||
}
|
||||
}
|
||||
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
package com.Modules.NormalData.cache;
|
||||
|
||||
import com.Modules.NormalData.compiled.CompiledRuleCache;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.data.redis.connection.DefaultMessage;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
class NormalizationCompiledCacheInvalidationBusTest {
|
||||
|
||||
@Test
|
||||
void clearsLocalRulesAndPublishesOneClusterCommand() {
|
||||
CompiledRuleCache cache = mock(CompiledRuleCache.class);
|
||||
StringRedisTemplate redis = mock(StringRedisTemplate.class);
|
||||
NormalizationCompiledCacheInvalidationBus bus =
|
||||
new NormalizationCompiledCacheInvalidationBus(cache, redis);
|
||||
|
||||
bus.invalidateRulesEverywhere(Arrays.asList(7L, 8L, 8L, null));
|
||||
|
||||
verify(cache).invalidateRule(7L);
|
||||
verify(cache).invalidateRule(8L);
|
||||
verify(redis).convertAndSend(
|
||||
NormalizationCompiledCacheInvalidationBus.TOPIC, "RULES:7,8");
|
||||
}
|
||||
|
||||
@Test
|
||||
void clearsAllLocallyAndPublishesClusterCommand() {
|
||||
CompiledRuleCache cache = mock(CompiledRuleCache.class);
|
||||
StringRedisTemplate redis = mock(StringRedisTemplate.class);
|
||||
NormalizationCompiledCacheInvalidationBus bus =
|
||||
new NormalizationCompiledCacheInvalidationBus(cache, redis);
|
||||
|
||||
bus.invalidateAllEverywhere();
|
||||
|
||||
verify(cache).invalidateAll();
|
||||
verify(redis).convertAndSend(
|
||||
NormalizationCompiledCacheInvalidationBus.TOPIC, "ALL");
|
||||
}
|
||||
|
||||
@Test
|
||||
void subscriberClearsCompiledCacheOnEveryInstance() {
|
||||
CompiledRuleCache cache = mock(CompiledRuleCache.class);
|
||||
NormalizationCompiledCacheInvalidationBus bus =
|
||||
new NormalizationCompiledCacheInvalidationBus(
|
||||
cache, mock(StringRedisTemplate.class));
|
||||
|
||||
bus.onMessage(message("RULES:7,8"), null);
|
||||
bus.onMessage(message("ALL"), null);
|
||||
|
||||
verify(cache).invalidateRule(7L);
|
||||
verify(cache).invalidateRule(8L);
|
||||
verify(cache).invalidateAll();
|
||||
}
|
||||
|
||||
private static DefaultMessage message(String body) {
|
||||
return new DefaultMessage(
|
||||
NormalizationCompiledCacheInvalidationBus.TOPIC.getBytes(StandardCharsets.UTF_8),
|
||||
body.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
package com.Modules.NormalData.cache;
|
||||
|
||||
import com.common.mapper.DeviceDeviceMapper;
|
||||
import com.common.mapper.DmNormalizeRuleMapper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
class NormalizationRuleCacheManagementServiceTest {
|
||||
|
||||
@Test
|
||||
void clearsDeviceRuleViewsEntitiesAndCompiledRules() {
|
||||
NormalizationCacheInvalidationService invalidation =
|
||||
mock(NormalizationCacheInvalidationService.class);
|
||||
NormalizationCompiledCacheInvalidationBus compiled =
|
||||
mock(NormalizationCompiledCacheInvalidationBus.class);
|
||||
DeviceDeviceMapper devices = mock(DeviceDeviceMapper.class);
|
||||
DmNormalizeRuleMapper rules = mock(DmNormalizeRuleMapper.class);
|
||||
when(rules.selectRuleIdsByDeviceId(40L)).thenReturn(Arrays.asList(7L, 8L, 8L));
|
||||
NormalizationRuleCacheManagementService service =
|
||||
new NormalizationRuleCacheManagementService(invalidation, compiled, devices, rules);
|
||||
|
||||
NormalizationRuleCacheManagementService.ClearResult result = service.clearDevice(40L);
|
||||
|
||||
assertEquals("DEVICE", result.getScope());
|
||||
assertEquals(40L, result.getDeviceId());
|
||||
assertEquals(2, result.getAssociatedRuleCount());
|
||||
verify(invalidation).invalidateRulesForDevice(40L);
|
||||
verify(invalidation).invalidateRuleEntity(7L);
|
||||
verify(invalidation).invalidateRuleEntity(8L);
|
||||
verify(compiled).invalidateRulesEverywhere(
|
||||
new java.util.LinkedHashSet<>(Arrays.asList(7L, 8L)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void clearsAllDistinctDeviceViewsAndCompiledCache() {
|
||||
NormalizationCacheInvalidationService invalidation =
|
||||
mock(NormalizationCacheInvalidationService.class);
|
||||
NormalizationCompiledCacheInvalidationBus compiled =
|
||||
mock(NormalizationCompiledCacheInvalidationBus.class);
|
||||
DeviceDeviceMapper devices = mock(DeviceDeviceMapper.class);
|
||||
DmNormalizeRuleMapper rules = mock(DmNormalizeRuleMapper.class);
|
||||
when(devices.selectAllIds()).thenReturn(Arrays.asList(40L, 41L));
|
||||
when(rules.selectAllAssociatedDeviceIds()).thenReturn(Arrays.asList(41L, 42L));
|
||||
NormalizationRuleCacheManagementService service =
|
||||
new NormalizationRuleCacheManagementService(invalidation, compiled, devices, rules);
|
||||
|
||||
NormalizationRuleCacheManagementService.ClearResult result = service.clearAll();
|
||||
|
||||
assertEquals("ALL", result.getScope());
|
||||
assertEquals(3, result.getDeviceCount());
|
||||
verify(invalidation).invalidateAllRules(
|
||||
org.mockito.ArgumentMatchers.argThat(ids -> ids.equals(
|
||||
new java.util.LinkedHashSet<>(Arrays.asList(40L, 41L, 42L)))));
|
||||
verify(compiled).invalidateAllEverywhere();
|
||||
}
|
||||
|
||||
@Test
|
||||
void acceptsDeviceWithoutAssociatedRules() {
|
||||
NormalizationCacheInvalidationService invalidation =
|
||||
mock(NormalizationCacheInvalidationService.class);
|
||||
NormalizationCompiledCacheInvalidationBus compiled =
|
||||
mock(NormalizationCompiledCacheInvalidationBus.class);
|
||||
DeviceDeviceMapper devices = mock(DeviceDeviceMapper.class);
|
||||
DmNormalizeRuleMapper rules = mock(DmNormalizeRuleMapper.class);
|
||||
when(rules.selectRuleIdsByDeviceId(40L)).thenReturn(Collections.emptyList());
|
||||
NormalizationRuleCacheManagementService service =
|
||||
new NormalizationRuleCacheManagementService(invalidation, compiled, devices, rules);
|
||||
|
||||
assertEquals(0, service.clearDevice(40L).getAssociatedRuleCount());
|
||||
verify(invalidation).invalidateRulesForDevice(40L);
|
||||
}
|
||||
}
|
||||
+18
@@ -101,6 +101,24 @@ class CompiledRuleCacheTest {
|
||||
assertEquals(4, compiler.compiles.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
void invalidatingAllRemovesEveryCompiledRule() {
|
||||
CountingCompiler compiler = new CountingCompiler();
|
||||
CompiledRuleCache cache = new CompiledRuleCache(compiler, properties());
|
||||
RuleSource first = source("v1", "{\"mappers\":[]}");
|
||||
RuleSource second = new RuleSource(9L, "rule-9", "rule-9", "json",
|
||||
"{\"mappers\":[]}", true, "0");
|
||||
cache.get(first, null);
|
||||
cache.get(second, null);
|
||||
assertEquals(2, compiler.compiles.get());
|
||||
|
||||
cache.invalidateAll();
|
||||
assertEquals(0L, cache.estimatedSize());
|
||||
cache.get(first, null);
|
||||
cache.get(second, null);
|
||||
assertEquals(4, compiler.compiles.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
void diagnosticRuleTextKeepsExecutionFieldsAndRemovesEmbeddedSamples() {
|
||||
String diagnostic = CompiledRuleCache.diagnosticRuleText("{"
|
||||
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package com.common.mapper;
|
||||
|
||||
import org.apache.ibatis.builder.xml.XMLMapperBuilder;
|
||||
import org.apache.ibatis.session.Configuration;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class NormalizationRuleCacheMapperXmlTest {
|
||||
|
||||
@Test
|
||||
void mapperXmlContainsCacheManagementQueries() throws Exception {
|
||||
Configuration configuration = new Configuration();
|
||||
parse(configuration, "mapper/DeviceDeviceMapper.xml");
|
||||
parse(configuration, "mapper/DmNormalizeRuleMapper.xml");
|
||||
|
||||
assertTrue(configuration.hasStatement(
|
||||
"com.common.mapper.DeviceDeviceMapper.selectAllIds"));
|
||||
assertTrue(configuration.hasStatement(
|
||||
"com.common.mapper.DmNormalizeRuleMapper.selectRuleIdsByDeviceId"));
|
||||
assertTrue(configuration.hasStatement(
|
||||
"com.common.mapper.DmNormalizeRuleMapper.selectAllAssociatedDeviceIds"));
|
||||
}
|
||||
|
||||
private static void parse(Configuration configuration, String resource) throws Exception {
|
||||
try (InputStream stream = Thread.currentThread().getContextClassLoader()
|
||||
.getResourceAsStream(resource)) {
|
||||
assertNotNull(stream, resource);
|
||||
new XMLMapperBuilder(stream, configuration, resource,
|
||||
configuration.getSqlFragments()).parse();
|
||||
}
|
||||
}
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
package com.controllers;
|
||||
|
||||
import com.Modules.NormalData.cache.NormalizationRuleCacheManagementService;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
class NormalizationRuleCacheControllerTest {
|
||||
|
||||
@Test
|
||||
void exposesDeviceAndAllRuleCacheOperations() {
|
||||
NormalizationRuleCacheManagementService service =
|
||||
mock(NormalizationRuleCacheManagementService.class);
|
||||
when(service.clearDevice(40L)).thenReturn(result("DEVICE", 40L, 1, 2));
|
||||
when(service.clearAll()).thenReturn(result("ALL", null, 8, 0));
|
||||
NormalizationRuleCacheController controller =
|
||||
new NormalizationRuleCacheController(service);
|
||||
|
||||
ResponseEntity<Map<String, Object>> device = controller.clearDevice(40L);
|
||||
Map<String, Object> all = controller.clearAll();
|
||||
|
||||
assertEquals(200, device.getStatusCodeValue());
|
||||
assertEquals(true, device.getBody().get("cleared"));
|
||||
assertEquals("DEVICE", device.getBody().get("scope"));
|
||||
assertEquals(40L, device.getBody().get("deviceId"));
|
||||
assertEquals("ALL", all.get("scope"));
|
||||
assertEquals(8, all.get("deviceCount"));
|
||||
assertFalse(all.containsKey("associatedRuleCount"));
|
||||
verify(service).clearDevice(40L);
|
||||
verify(service).clearAll();
|
||||
}
|
||||
|
||||
@Test
|
||||
void rejectsNonPositiveDeviceIdWithoutCallingService() {
|
||||
NormalizationRuleCacheManagementService service =
|
||||
mock(NormalizationRuleCacheManagementService.class);
|
||||
NormalizationRuleCacheController controller =
|
||||
new NormalizationRuleCacheController(service);
|
||||
|
||||
ResponseEntity<Map<String, Object>> response = controller.clearDevice(0L);
|
||||
|
||||
assertEquals(400, response.getStatusCodeValue());
|
||||
assertEquals("INVALID_DEVICE_ID", response.getBody().get("errorCode"));
|
||||
}
|
||||
|
||||
private static NormalizationRuleCacheManagementService.ClearResult result(
|
||||
String scope, Long deviceId, int deviceCount, int ruleCount) {
|
||||
return new NormalizationRuleCacheManagementService.ClearResult(
|
||||
scope, deviceId, deviceCount, ruleCount);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user