From 8ca73145632b223a09d6ca781cd64e242305a584 Mon Sep 17 00:00:00 2001 From: mcrcortex <18544518+MCRcortex@users.noreply.github.com> Date: Sat, 27 Jan 2024 00:22:29 +1000 Subject: [PATCH] Made the fragmented storage replicate mappings to all dbs, this should mean that repair after corruption is 30x more likly --- .../FragmentedStorageBackendAdaptor.java | 63 ++++++++++++++++++- 1 file changed, 61 insertions(+), 2 deletions(-) diff --git a/src/main/java/me/cortex/zenith/common/world/storage/FragmentedStorageBackendAdaptor.java b/src/main/java/me/cortex/zenith/common/world/storage/FragmentedStorageBackendAdaptor.java index ffbad877..27b89084 100644 --- a/src/main/java/me/cortex/zenith/common/world/storage/FragmentedStorageBackendAdaptor.java +++ b/src/main/java/me/cortex/zenith/common/world/storage/FragmentedStorageBackendAdaptor.java @@ -1,6 +1,9 @@ package me.cortex.zenith.common.world.storage; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.ints.Int2ShortOpenHashMap; +import it.unimi.dsi.fastutil.objects.Object2IntMap; +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import me.cortex.zenith.common.world.storage.lmdb.LMDBStorageBackend; import net.minecraft.util.math.random.RandomSeed; @@ -8,6 +11,7 @@ import java.io.File; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.file.Files; +import java.util.Arrays; //Segments the section data into multiple dbs public class FragmentedStorageBackendAdaptor extends StorageBackend { @@ -57,12 +61,67 @@ public class FragmentedStorageBackendAdaptor extends StorageBackend { @Override public void putIdMapping(int id, ByteBuffer data) { - this.backends[0].putIdMapping(id, data); + //Replicate the mappings over all the dbs to mean the chance of recovery in case of corruption is 30x + for (var backend : this.backends) { + backend.putIdMapping(id, data); + } + } + + private record EqualingArray(byte[] bytes) { + @Override + public boolean equals(Object obj) { + return Arrays.equals(this.bytes, ((EqualingArray)obj).bytes); + } + + @Override + public int hashCode() { + return Arrays.hashCode(this.bytes); + } } @Override public Int2ObjectOpenHashMap getIdMappingsData() { - return this.backends[0].getIdMappingsData(); + Object2IntOpenHashMap> verification = new Object2IntOpenHashMap<>(); + Int2ObjectOpenHashMap any = null; + for (var backend : this.backends) { + var mappings = backend.getIdMappingsData(); + if (mappings.isEmpty()) { + //TODO: log a warning and attempt to replicate the data the other fragments + continue; + } + var repackaged = new Int2ObjectOpenHashMap(mappings.size()); + for (var entry : mappings.int2ObjectEntrySet()) { + repackaged.put(entry.getIntKey(), new EqualingArray(entry.getValue())); + } + verification.addTo(repackaged, 1); + any = repackaged; + } + if (verification.size() != 1) { + System.err.println("Error id mapping not matching across all fragments, attempting to recover"); + Object2IntMap.Entry> maxEntry = null; + for (var entry : verification.object2IntEntrySet()) { + if (maxEntry == null) { maxEntry = entry; } + else { + if (maxEntry.getIntValue() < entry.getIntValue()) { + maxEntry = entry; + } + } + } + + var mapping = maxEntry.getKey(); + + var out = new Int2ObjectOpenHashMap(mapping.size()); + for (var entry : mapping.int2ObjectEntrySet()) { + out.put(entry.getIntKey(), entry.getValue().bytes); + } + return out; + } else { + var out = new Int2ObjectOpenHashMap(any.size()); + for (var entry : any.int2ObjectEntrySet()) { + out.put(entry.getIntKey(), entry.getValue().bytes); + } + return out; + } } @Override