FileStoreRecordingStorageProbe.java
package org.hammer.audio.recording.runtime;
import java.io.IOException;
import java.nio.file.FileStore;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Instant;
import java.util.Objects;
/** Recording-storage probe backed by {@link FileStore}. */
public final class FileStoreRecordingStorageProbe implements RecordingStorageProbe {
static final long MINIMUM_WARNING_BYTES = 256L * 1024L * 1024L;
static final long MINIMUM_CRITICAL_BYTES = 64L * 1024L * 1024L;
static final long WARNING_SECONDS = 300L;
static final long CRITICAL_SECONDS = 30L;
@Override
public RecordingStorageStatus probe(
Path destination,
long bytesWritten,
double measuredBytesPerSecond,
double expectedBytesPerSecond,
Instant checkedAt)
throws IOException {
Objects.requireNonNull(destination, "destination");
Objects.requireNonNull(checkedAt, "checkedAt");
Path normalized = destination.toAbsolutePath().normalize();
Path existing = nearestExistingPath(normalized);
FileStore store = Files.getFileStore(existing);
long usable = store.getUsableSpace();
long unallocated = store.getUnallocatedSpace();
long total = store.getTotalSpace();
Path writablePath = Files.isDirectory(existing) ? existing : existing.getParent();
boolean writable = writablePath != null && Files.isWritable(writablePath);
double effectiveRate =
measuredBytesPerSecond > 0.0 ? measuredBytesPerSecond : expectedBytesPerSecond;
long remainingSeconds = effectiveRate > 0.0 ? (long) Math.floor(usable / effectiveRate) : -1L;
long warningBytes =
Math.max(MINIMUM_WARNING_BYTES, safeMultiply(expectedBytesPerSecond, WARNING_SECONDS));
long criticalBytes =
Math.max(MINIMUM_CRITICAL_BYTES, safeMultiply(expectedBytesPerSecond, CRITICAL_SECONDS));
RecordingStorageLevel level;
if (!writable
|| usable <= criticalBytes
|| (remainingSeconds >= 0 && remainingSeconds <= CRITICAL_SECONDS)) {
level = RecordingStorageLevel.CRITICAL;
} else if (usable <= warningBytes
|| (remainingSeconds >= 0 && remainingSeconds <= WARNING_SECONDS)) {
level = RecordingStorageLevel.WARNING;
} else {
level = RecordingStorageLevel.NORMAL;
}
return new RecordingStorageStatus(
normalized,
store.name(),
store.type(),
writable,
usable,
unallocated,
total,
bytesWritten,
measuredBytesPerSecond,
expectedBytesPerSecond,
remainingSeconds,
level,
checkedAt,
"");
}
private static Path nearestExistingPath(Path path) throws IOException {
Path candidate = path;
while (candidate != null && !Files.exists(candidate)) {
candidate = candidate.getParent();
}
if (candidate == null) {
throw new IOException("No existing ancestor for recording destination: " + path);
}
return candidate;
}
private static long safeMultiply(double bytesPerSecond, long seconds) {
if (bytesPerSecond <= 0.0 || !Double.isFinite(bytesPerSecond)) {
return 0L;
}
double value = bytesPerSecond * seconds;
return value >= Long.MAX_VALUE ? Long.MAX_VALUE : (long) Math.ceil(value);
}
}