FileChannel.truncate reduces a file to a requested size when it is larger. A recovery routine must validate that offset against a known complete-record boundary before deleting the tail.
Java FileChannel.truncate: cut only after validating the recovery offset
Operational contract
The method refuses negative offsets and offsets beyond the current file size, then truncates and forces the channel. It cannot discover the last valid record by itself. That boundary must come from a separately validated record format or checksum scan. Truncation is destructive, and a concurrent writer can make a size observation stale. Stop writers and capture a backup or quarantine copy before recovery. A channel's current position may be adjusted by truncation, so code that continues writing should set its intended position explicitly.
Failure case
An append-only receipt log ends with a torn record. A scanner verifies all complete records through byte 8,192 and reports that exact offset. Recovery stops writers, keeps a copy of the damaged tail for inspection, and cuts the active log at 8,192. Guessing the offset from the last newline would be unsafe for a binary format.
Java code
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
public class ReceiptLogTailRecovery {
public static void cutToValidatedBoundary(Path log, long completeBytes) throws IOException {
if (completeBytes < 0) throw new IllegalArgumentException("Negative boundary");
try (FileChannel channel = FileChannel.open(log, StandardOpenOption.WRITE)) {
if (completeBytes > channel.size()) {
throw new IllegalArgumentException("Boundary is beyond current file");
}
channel.truncate(completeBytes);
channel.force(true);
}
}
}Performance and ownership cost
The API request is O(1) in application work, but filesystem work and a forced flush can be expensive. Validating the last complete record may require O(N) scanning. Never trade that validation away to make recovery appear faster.
Common Mistakes
- Do not infer a valid record boundary from size alone.
- Do not truncate while an uncontrolled writer is appending.
- Do not assume the channel position remains useful for the next write.
Connected lessons
- Java FileChannel.force: distinguish written bytes from forced storage
- Java Files.move: atomic publication is a filesystem contract
- Java bounded line reader: reject oversized records without buffering the file
- Java FileChannel.write: finish the buffer before reporting success
- Java positioned FileChannel.read: detect a short record without moving the cursor
- Java FileChannel.transferTo: verify the byte count on every pass
- Java FileLock: coordinate one byte range with a shared protocol
- Java AsynchronousFileChannel: own the buffer through completion
- Java file channels and JVM observations quiz
- Advanced Java
