DataStoreRecoveryTest.java
/*******************************************************************************
* Copyright (c) 2025 Eclipse RDF4J contributors.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Distribution License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: BSD-3-Clause
*******************************************************************************/
package org.eclipse.rdf4j.sail.nativerdf.datastore;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import java.io.File;
import java.io.RandomAccessFile;
import org.eclipse.rdf4j.sail.nativerdf.NativeStore;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.junit.jupiter.api.parallel.Isolated;
/**
* Tests recovery in DataStore.getData when the stored data length is zero but neighboring ID offsets exist. The
* recovery uses the next ID's offset to infer the correct data length.
*/
@Isolated
public class DataStoreRecoveryTest {
@TempDir
File tempDir;
private boolean previousSoftFlag;
@BeforeEach
public void setup() {
previousSoftFlag = NativeStore.SOFT_FAIL_ON_CORRUPT_DATA_AND_REPAIR_INDEXES;
NativeStore.SOFT_FAIL_ON_CORRUPT_DATA_AND_REPAIR_INDEXES = true;
}
@AfterEach
public void teardown() {
NativeStore.SOFT_FAIL_ON_CORRUPT_DATA_AND_REPAIR_INDEXES = previousSoftFlag;
}
@Test
public void recoversDataUsingNextOffsetWhenLengthIsZero() throws Exception {
DataStore ds = new DataStore(tempDir, "values");
byte[] d1 = new byte[] { 1, 2, 3, 4, 5 };
byte[] d2 = new byte[] { 9, 8, 7 };
int id1 = ds.storeData(d1);
int id2 = ds.storeData(d2);
ds.sync();
// Corrupt the first record's length to zero
IDFile idFile = new IDFile(new File(tempDir, "values.id"));
long off1 = idFile.getOffset(id1);
try (RandomAccessFile raf = new RandomAccessFile(new File(tempDir, "values.dat"), "rw")) {
raf.seek(off1);
raf.write(new byte[] { 0, 0, 0, 0 });
}
// Now ds.getData(id1) should throw with recovered data
try {
ds.getData(id1);
} catch (RecoveredDataException rde) {
assertArrayEquals(d1, rde.getData(), "Recovered data should match original bytes");
return;
}
throw new AssertionError("Expected RecoveredDataException to be thrown");
}
}