aboutsummaryrefslogtreecommitdiffstats
path: root/node-admin/src/main/java
diff options
context:
space:
mode:
authorValerij Fredriksen <valerijf@verizonmedia.com>2019-09-27 14:47:45 +0200
committerValerij Fredriksen <valerijf@verizonmedia.com>2020-04-22 15:24:51 +0200
commitd2090d1f4b4a6c56fb528d819af7ee5b680f3ef7 (patch)
tree1ee6cea55e3a9e458de33e658b0e5333358b5dca /node-admin/src/main/java
parent95a6fc2366a2348f378ffc1c251296fc95a80726 (diff)
Create DiskCleanup
Diffstat (limited to 'node-admin/src/main/java')
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/disk/CoredumpCleanupRule.java106
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/disk/DiskCleanup.java67
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/disk/DiskCleanupRule.java31
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/disk/LinearCleanupRule.java48
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/disk/package-info.java5
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/file/FileFinder.java2
6 files changed, 258 insertions, 1 deletions
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/disk/CoredumpCleanupRule.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/disk/CoredumpCleanupRule.java
new file mode 100644
index 00000000000..2ad887514e5
--- /dev/null
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/disk/CoredumpCleanupRule.java
@@ -0,0 +1,106 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.node.admin.maintenance.disk;
+
+import com.yahoo.vespa.hosted.node.admin.maintenance.coredump.CoredumpHandler;
+import com.yahoo.vespa.hosted.node.admin.task.util.file.FileFinder;
+
+import java.nio.file.Path;
+import java.time.Instant;
+import java.time.ZoneOffset;
+import java.time.temporal.ChronoField;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+import static com.yahoo.vespa.hosted.node.admin.maintenance.disk.DiskCleanupRule.PrioritizedFileAttributes;
+import static com.yahoo.vespa.hosted.node.admin.maintenance.disk.DiskCleanupRule.Priority;
+import static com.yahoo.vespa.hosted.node.admin.task.util.file.FileFinder.FileAttributes;
+import static com.yahoo.vespa.hosted.node.admin.task.util.file.FileFinder.nameStartsWith;
+
+/**
+ * @author freva
+ */
+public class CoredumpCleanupRule {
+
+ private static final Comparator<FileAttributes> CORE_DUMP_FILE_ATTRIBUTE_COMPARATOR = Comparator
+ .comparing((FileAttributes fa) -> !fa.filename().contains("vespa-"))
+ .thenComparing(FileAttributes::lastModifiedTime);
+
+ public static DiskCleanupRule forContainer(Path containerCrashPath) {
+ return new ContainerCoredumpCleanupRule(containerCrashPath);
+ }
+
+ public static DiskCleanupRule forHost(Path processedCoredumpsPath) {
+ return new HostCoredumpCleanupRule(processedCoredumpsPath);
+ }
+
+ /** Assigns MEDIUM priority to the oldest, unprocessed coredump and HIGHEST for the remaining */
+ private static class ContainerCoredumpCleanupRule implements DiskCleanupRule {
+ private final Path containerCrashPath;
+
+ private ContainerCoredumpCleanupRule(Path containerCrashPath) {
+ this.containerCrashPath = containerCrashPath;
+ }
+
+ @Override
+ public Collection<PrioritizedFileAttributes> prioritize() {
+ List<FileAttributes> fileAttributes = FileFinder.files(containerCrashPath)
+ .maxDepth(1).stream()
+ .sorted(CORE_DUMP_FILE_ATTRIBUTE_COMPARATOR)
+ .collect(Collectors.toList());
+
+ return mapFirstAndRemaining(fileAttributes, Priority.MEDIUM, Priority.HIGHEST).collect(Collectors.toList());
+ }
+ }
+
+ /** Assigns MEDIUM priority to the first coredump of the day for each container, HIGH for the remaining */
+ private static class HostCoredumpCleanupRule implements DiskCleanupRule {
+ private final Path processedCoredumpsPath;
+
+ private HostCoredumpCleanupRule(Path processedCoredumpsPath) {
+ this.processedCoredumpsPath = processedCoredumpsPath;
+ }
+
+ @Override
+ public Collection<PrioritizedFileAttributes> prioritize() {
+ Map<String, List<FileAttributes>> fileAttributesByContainerDay = FileFinder.files(processedCoredumpsPath)
+ .match(nameStartsWith(CoredumpHandler.COREDUMP_FILENAME_PREFIX))
+ .stream()
+ .sorted(CORE_DUMP_FILE_ATTRIBUTE_COMPARATOR)
+ .collect(Collectors.groupingBy(
+ // Group FileAttributes by string [container-name]_[day of year], e.g. zt00534-v6-2_234
+ fa -> containerNameFromProcessedCoredumpPath(fa.path()) + "_" + dayOfYear(fa.lastModifiedTime()),
+ Collectors.collectingAndThen(
+ Collectors.toCollection(ArrayList::new),
+ l -> { l.sort(CORE_DUMP_FILE_ATTRIBUTE_COMPARATOR); return l; } )));
+
+ return fileAttributesByContainerDay.values().stream()
+ .flatMap(fa -> mapFirstAndRemaining(fa, Priority.MEDIUM, Priority.HIGH))
+ .collect(Collectors.toList());
+ }
+ }
+
+ /**
+ * Maps list of FileAttributes into list of PrioritizedFileAttributes where the first FileAttribute is given
+ * {@code first} priority, while the remaining FileAttributes are given {@code remaining} priority */
+ private static Stream<PrioritizedFileAttributes> mapFirstAndRemaining(List<FileAttributes> fileAttributes, Priority first, Priority remaining) {
+ return IntStream.range(0, fileAttributes.size())
+ .mapToObj(i -> new PrioritizedFileAttributes(fileAttributes.get(i), i == 0 ? first : remaining));
+ }
+
+ /** Extracts container-name from path under processed-coredumps or empty string */
+ private static String containerNameFromProcessedCoredumpPath(Path path) {
+ if (path.getNameCount() < 3) return ""; // Path is too short
+ return path.getName(path.getNameCount() - 3).toString();
+ }
+
+ /** Returns day number of the year (1-365 (or 366 for leap years)) */
+ private static int dayOfYear(Instant instant) {
+ return instant.atOffset(ZoneOffset.UTC).get(ChronoField.DAY_OF_YEAR);
+ }
+}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/disk/DiskCleanup.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/disk/DiskCleanup.java
new file mode 100644
index 00000000000..a9c1f8a9600
--- /dev/null
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/disk/DiskCleanup.java
@@ -0,0 +1,67 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.node.admin.maintenance.disk;
+
+import com.yahoo.vespa.hosted.node.admin.component.TaskContext;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.logging.Logger;
+
+import static com.yahoo.vespa.hosted.node.admin.maintenance.disk.DiskCleanupRule.PrioritizedFileAttributes;
+import static com.yahoo.yolean.Exceptions.uncheck;
+
+/**
+ * @author freva
+ */
+public class DiskCleanup {
+
+ private static final Logger logger = Logger.getLogger(DiskCleanup.class.getName());
+ private static final char[] UNITS = "kMGTPE".toCharArray();
+ private static final Comparator<PrioritizedFileAttributes> PRIORITIZED_FILE_ATTRIBUTES_COMPARATOR = Comparator
+ .comparing(PrioritizedFileAttributes::priority)
+ .thenComparingLong(f -> f.fileAttributes().size())
+ .reversed();
+
+ public boolean cleanup(TaskContext context, List<DiskCleanupRule> rules, long bytesToRemove) {
+ if (bytesToRemove <= 0) return false;
+
+ long[] btr = new long[] { bytesToRemove };
+ List<Path> deletedPaths = new ArrayList<>();
+ try {
+ rules.stream()
+ .flatMap(rule -> rule.prioritize().stream())
+ .sorted(PRIORITIZED_FILE_ATTRIBUTES_COMPARATOR)
+ .takeWhile(fa -> btr[0] > 0)
+ .forEach(pfa -> {
+ if (uncheck(() -> Files.deleteIfExists(pfa.fileAttributes().path()))) {
+ btr[0] -= pfa.fileAttributes().size();
+ deletedPaths.add(pfa.fileAttributes().path());
+ }
+ });
+
+ } finally {
+ String wantedDeleteSize = bytesToDisplayCount(bytesToRemove);
+ String deletedSize = bytesToDisplayCount(bytesToRemove - btr[0]);
+ if (deletedPaths.size() > 20) {
+ context.log(logger, "Deleted %d files (%s) because disk was getting full", deletedPaths.size(), deletedSize);
+ } else if (deletedPaths.size() > 0) {
+ context.log(logger, "Deleted %s because disk was getting full from: %s", deletedSize, deletedPaths);
+ } else {
+ context.log(logger, "Wanted to delete %s, but failed to find any files to delete", wantedDeleteSize);
+ }
+ }
+
+ return !deletedPaths.isEmpty();
+ }
+
+ static String bytesToDisplayCount(long bytes) {
+ if (bytes < 1000) return bytes + " bytes";
+
+ int unit = -1;
+ for (; bytes >= 1000; unit++) bytes /= 1000;
+ return bytes + " " + UNITS[unit] + "B";
+ }
+}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/disk/DiskCleanupRule.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/disk/DiskCleanupRule.java
new file mode 100644
index 00000000000..a4c8229d0d7
--- /dev/null
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/disk/DiskCleanupRule.java
@@ -0,0 +1,31 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.node.admin.maintenance.disk;
+
+import com.yahoo.vespa.hosted.node.admin.task.util.file.FileFinder;
+
+import java.util.Collection;
+
+/**
+ * @author freva
+ */
+public interface DiskCleanupRule {
+
+ Collection<PrioritizedFileAttributes> prioritize();
+
+ enum Priority {
+ LOWEST, LOW, MEDIUM, HIGH, HIGHEST
+ }
+
+ class PrioritizedFileAttributes {
+ private final FileFinder.FileAttributes fileAttributes;
+ private final Priority priority;
+
+ public PrioritizedFileAttributes(FileFinder.FileAttributes fileAttributes, Priority priority) {
+ this.fileAttributes = fileAttributes;
+ this.priority = priority;
+ }
+
+ public Priority priority() { return priority; }
+ public FileFinder.FileAttributes fileAttributes() { return fileAttributes; }
+ }
+}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/disk/LinearCleanupRule.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/disk/LinearCleanupRule.java
new file mode 100644
index 00000000000..3ef4e2e4f6a
--- /dev/null
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/disk/LinearCleanupRule.java
@@ -0,0 +1,48 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.node.admin.maintenance.disk;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+
+import static com.yahoo.vespa.hosted.node.admin.task.util.file.FileFinder.FileAttributes;
+
+/**
+ * Prioritizes files by first scoring them with the given scoring function and then mapping the scores to a
+ * priority within the given range.
+ * The priority room is evenly split between given lowest and highest priority for range [0, 1.0). Scores below 0
+ * are assigned lowest, while scores at or higher than 1 are assigned highest priority.
+ *
+ * Typical use-case is for log files. The scoring function calculates the file age and normalizes it by dividing it
+ * by expected max age of log files. The oldest logs will then by prioritized by highest given priority.
+ *
+ * @author freva
+ */
+public class LinearCleanupRule implements DiskCleanupRule {
+ private final Supplier<List<FileAttributes>> lister;
+ private final Function<FileAttributes, Priority> prioritizer;
+
+ public LinearCleanupRule(Supplier<List<FileAttributes>> lister,
+ Function<FileAttributes, Double> scorer, Priority lowest, Priority highest) {
+ if (lowest.ordinal() > highest.ordinal())
+ throw new IllegalArgumentException("Lowest priority: " + lowest + " is higher than highest priority: " + highest);
+
+ this.lister = lister;
+
+ Priority[] values = Priority.values();
+ int range = highest.ordinal() - lowest.ordinal() + 1;
+ this.prioritizer = fa -> {
+ int ordinal = (int) (lowest.ordinal() + scorer.apply(fa) * range);
+ return values[Math.max(lowest.ordinal(), Math.min(highest.ordinal(), ordinal))];
+ };
+ }
+
+ @Override
+ public Collection<PrioritizedFileAttributes> prioritize() {
+ return lister.get().stream()
+ .map(fa -> new PrioritizedFileAttributes(fa, prioritizer.apply(fa)))
+ .collect(Collectors.toList());
+ }
+}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/disk/package-info.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/disk/package-info.java
new file mode 100644
index 00000000000..dff40015522
--- /dev/null
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/disk/package-info.java
@@ -0,0 +1,5 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+@ExportPackage
+package com.yahoo.vespa.hosted.node.admin.maintenance.disk;
+
+import com.yahoo.osgi.annotation.ExportPackage;
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/file/FileFinder.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/file/FileFinder.java
index f0b064373d2..a41281d0479 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/file/FileFinder.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/file/FileFinder.java
@@ -193,7 +193,7 @@ public class FileFinder {
private final Path path;
private final BasicFileAttributes attributes;
- FileAttributes(Path path, BasicFileAttributes attributes) {
+ public FileAttributes(Path path, BasicFileAttributes attributes) {
this.path = path;
this.attributes = attributes;
}