|
@@ -163,6 +163,58 @@ public class AdminController {
|
|
|
);
|
|
);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ @PostMapping("/redis/delete-bid-media-expiring-before")
|
|
|
|
|
+ public Object deleteBidMediaExpiringBefore(@RequestBody(required = false) Map<String, Object> body) {
|
|
|
|
|
+ if (redisTemplate == null) {
|
|
|
|
|
+ throw new ResponseStatusException(HttpStatus.SERVICE_UNAVAILABLE,
|
|
|
|
|
+ "redis template is not configured");
|
|
|
|
|
+ }
|
|
|
|
|
+ long ttlLessThanSeconds = toLong(body == null ? null : body.get("ttlLessThanSeconds"));
|
|
|
|
|
+ if (ttlLessThanSeconds <= 0) {
|
|
|
|
|
+ ttlLessThanSeconds = Duration.ofDays(2).getSeconds();
|
|
|
|
|
+ }
|
|
|
|
|
+ long scanCount = toLong(body == null ? null : body.get("scanCount"));
|
|
|
|
|
+ if (scanCount <= 0) {
|
|
|
|
|
+ scanCount = 1000;
|
|
|
|
|
+ }
|
|
|
|
|
+ long maxKeys = toLong(body == null ? null : body.get("maxKeys"));
|
|
|
|
|
+ if (maxKeys <= 0) {
|
|
|
|
|
+ maxKeys = 50000;
|
|
|
|
|
+ }
|
|
|
|
|
+ List<String> patterns = parseStringList(body == null ? null : body.get("patterns"));
|
|
|
|
|
+ if (patterns.isEmpty()) {
|
|
|
|
|
+ patterns = defaultBidMediaPatterns();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ List<Map<String, Object>> results = new ArrayList<>();
|
|
|
|
|
+ long totalScanned = 0;
|
|
|
|
|
+ long totalDeleted = 0;
|
|
|
|
|
+ for (String pattern : patterns) {
|
|
|
|
|
+ DeleteExpiringResult result = deleteExpiringKeys(pattern, ttlLessThanSeconds, scanCount,
|
|
|
|
|
+ Math.max(0, maxKeys - totalScanned));
|
|
|
|
|
+ results.add(Map.of(
|
|
|
|
|
+ "pattern", pattern,
|
|
|
|
|
+ "scanned", result.scanned(),
|
|
|
|
|
+ "deleted", result.deleted(),
|
|
|
|
|
+ "stoppedByLimit", result.stoppedByLimit()
|
|
|
|
|
+ ));
|
|
|
|
|
+ totalScanned += result.scanned();
|
|
|
|
|
+ totalDeleted += result.deleted();
|
|
|
|
|
+ if (totalScanned >= maxKeys) {
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return Map.of(
|
|
|
|
|
+ "ttlLessThanSeconds", ttlLessThanSeconds,
|
|
|
|
|
+ "ttlLessThanHuman", Duration.ofSeconds(ttlLessThanSeconds).toString(),
|
|
|
|
|
+ "scanCount", scanCount,
|
|
|
|
|
+ "maxKeys", maxKeys,
|
|
|
|
|
+ "scanned", totalScanned,
|
|
|
|
|
+ "deleted", totalDeleted,
|
|
|
|
|
+ "patterns", results
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
private static int toInt(Object v) {
|
|
private static int toInt(Object v) {
|
|
|
if (v == null) return 0;
|
|
if (v == null) return 0;
|
|
|
if (v instanceof Number n) return n.intValue();
|
|
if (v instanceof Number n) return n.intValue();
|
|
@@ -201,6 +253,32 @@ public class AdminController {
|
|
|
return new TtlTightenResult(scanned, updated, false);
|
|
return new TtlTightenResult(scanned, updated, false);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ private DeleteExpiringResult deleteExpiringKeys(String pattern, long ttlLessThanSeconds, long scanCount, long maxKeys) {
|
|
|
|
|
+ if (maxKeys <= 0 || pattern == null || pattern.isBlank()) {
|
|
|
|
|
+ return new DeleteExpiringResult(0, 0, true);
|
|
|
|
|
+ }
|
|
|
|
|
+ long scanned = 0;
|
|
|
|
|
+ long deleted = 0;
|
|
|
|
|
+ ScanOptions options = ScanOptions.scanOptions().match(pattern).count(scanCount).build();
|
|
|
|
|
+ try (Cursor<String> cursor = redisTemplate.scan(options)) {
|
|
|
|
|
+ while (cursor.hasNext()) {
|
|
|
|
|
+ String key = cursor.next();
|
|
|
|
|
+ scanned++;
|
|
|
|
|
+ Long currentTtl = redisTemplate.getExpire(key, TimeUnit.SECONDS);
|
|
|
|
|
+ if (currentTtl != null && currentTtl > 0 && currentTtl < ttlLessThanSeconds) {
|
|
|
|
|
+ Boolean ok = redisTemplate.delete(key);
|
|
|
|
|
+ if (Boolean.TRUE.equals(ok)) {
|
|
|
|
|
+ deleted++;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ if (scanned >= maxKeys) {
|
|
|
|
|
+ return new DeleteExpiringResult(scanned, deleted, true);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return new DeleteExpiringResult(scanned, deleted, false);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
private static List<String> parseStringList(Object raw) {
|
|
private static List<String> parseStringList(Object raw) {
|
|
|
if (!(raw instanceof Iterable<?> iterable)) {
|
|
if (!(raw instanceof Iterable<?> iterable)) {
|
|
|
return List.of();
|
|
return List.of();
|
|
@@ -247,4 +325,6 @@ public class AdminController {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private record TtlTightenResult(long scanned, long updated, boolean stoppedByLimit) {}
|
|
private record TtlTightenResult(long scanned, long updated, boolean stoppedByLimit) {}
|
|
|
|
|
+
|
|
|
|
|
+ private record DeleteExpiringResult(long scanned, long deleted, boolean stoppedByLimit) {}
|
|
|
}
|
|
}
|