yumeng 3 nedēļas atpakaļ
vecāks
revīzija
acf3842d49

+ 80 - 0
src/main/java/com/adx/tencent/httpapi/AdminController.java

@@ -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) {
         if (v == null) return 0;
         if (v instanceof Number n) return n.intValue();
@@ -201,6 +253,32 @@ public class AdminController {
         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) {
         if (!(raw instanceof Iterable<?> iterable)) {
             return List.of();
@@ -247,4 +325,6 @@ public class AdminController {
     }
 
     private record TtlTightenResult(long scanned, long updated, boolean stoppedByLimit) {}
+
+    private record DeleteExpiringResult(long scanned, long deleted, boolean stoppedByLimit) {}
 }