mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2024-12-14 20:20:30 +00:00
Try to ensure atomic writes to our ID file
- We now write to a "ids.json.new" file, then move that on top of the original ids.json file instead. - Use FileChannel.force to ensure the new file is properly flushed to disk. I can't really guarantee this'll work with the later Files.move, but it's better than not doing it! Closes #1346.
This commit is contained in:
parent
c9bb534799
commit
566315947b
@ -12,13 +12,15 @@ import org.slf4j.Logger;
|
|||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
import java.io.BufferedWriter;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
import java.io.Writer;
|
import java.io.Writer;
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
|
import java.nio.channels.Channels;
|
||||||
|
import java.nio.channels.FileChannel;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.*;
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@ -31,10 +33,13 @@ public final class IDAssigner {
|
|||||||
}.getType();
|
}.getType();
|
||||||
|
|
||||||
private final Path idFile;
|
private final Path idFile;
|
||||||
|
private final Path newIdFile;
|
||||||
|
private boolean atomicMove = true;
|
||||||
private @Nullable Map<String, Integer> ids;
|
private @Nullable Map<String, Integer> ids;
|
||||||
|
|
||||||
public IDAssigner(Path path) {
|
public IDAssigner(Path path) {
|
||||||
idFile = path;
|
idFile = path;
|
||||||
|
newIdFile = path.resolveSibling(path.getFileName() + ".new");
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized int getNextId(String kind) {
|
public synchronized int getNextId(String kind) {
|
||||||
@ -44,9 +49,22 @@ public final class IDAssigner {
|
|||||||
var next = existing == null ? 0 : existing + 1;
|
var next = existing == null ? 0 : existing + 1;
|
||||||
ids.put(kind, next);
|
ids.put(kind, next);
|
||||||
|
|
||||||
// We've changed the ID file, so save it back again.
|
// We've changed the ID file, so save it back again. We save to a temporary ".new" file, then move that over the
|
||||||
try (Writer writer = Files.newBufferedWriter(idFile, StandardCharsets.UTF_8)) {
|
// original. This should reduce the risk of corrupting the file if Minecraft (or the computer!) is stopped.
|
||||||
GSON.toJson(ids, writer);
|
try {
|
||||||
|
try (var channel = FileChannel.open(newIdFile, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE)) {
|
||||||
|
Writer writer = new BufferedWriter(Channels.newWriter(channel, StandardCharsets.UTF_8));
|
||||||
|
GSON.toJson(ids, writer);
|
||||||
|
writer.flush();
|
||||||
|
|
||||||
|
channel.force(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Files.move(newIdFile, idFile, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
|
||||||
|
} catch (AtomicMoveNotSupportedException | UnsupportedOperationException e) {
|
||||||
|
Files.move(newIdFile, idFile, StandardCopyOption.REPLACE_EXISTING);
|
||||||
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
LOG.error("Cannot update ID file '{}'", idFile, e);
|
LOG.error("Cannot update ID file '{}'", idFile, e);
|
||||||
}
|
}
|
||||||
@ -57,7 +75,11 @@ public final class IDAssigner {
|
|||||||
private Map<String, Integer> loadIds() {
|
private Map<String, Integer> loadIds() {
|
||||||
if (Files.isRegularFile(idFile)) {
|
if (Files.isRegularFile(idFile)) {
|
||||||
try (Reader reader = Files.newBufferedReader(idFile, StandardCharsets.UTF_8)) {
|
try (Reader reader = Files.newBufferedReader(idFile, StandardCharsets.UTF_8)) {
|
||||||
return GSON.fromJson(reader, ID_TOKEN);
|
Map<String, Integer> result = GSON.fromJson(reader, ID_TOKEN);
|
||||||
|
if (result != null) return result;
|
||||||
|
|
||||||
|
// This happens when the file is empty. Odd, I know!
|
||||||
|
LOG.error("ID file {} is corrupted, computer IDs may be duplicated", idFile);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LOG.error("Cannot load id file '" + idFile + "'", e);
|
LOG.error("Cannot load id file '" + idFile + "'", e);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user