Skip to content

Commit 475d09e

Browse files
committed
GH-5148 Introduce "soft fail" for corrupt ValueStore
1 parent d05b511 commit 475d09e

2 files changed

Lines changed: 116 additions & 1 deletion

File tree

core/sail/nativerdf/src/main/java/org/eclipse/rdf4j/sail/nativerdf/ValueStore.java

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import org.eclipse.rdf4j.model.vocabulary.XSD;
3434
import org.eclipse.rdf4j.sail.SailException;
3535
import org.eclipse.rdf4j.sail.nativerdf.datastore.DataStore;
36+
import org.eclipse.rdf4j.sail.nativerdf.model.CorruptValue;
3637
import org.eclipse.rdf4j.sail.nativerdf.model.NativeBNode;
3738
import org.eclipse.rdf4j.sail.nativerdf.model.NativeIRI;
3839
import org.eclipse.rdf4j.sail.nativerdf.model.NativeLiteral;
@@ -123,6 +124,11 @@ public class ValueStore extends SimpleValueFactory {
123124
*/
124125
private final ConcurrentCache<String, Integer> namespaceIDCache;
125126

127+
/**
128+
* Do not throw an exception in case a value cannot be loaded, e.g. due to a corrupt value store.
129+
*/
130+
private final boolean softFailOnCorruptData;
131+
126132
/*--------------*
127133
* Constructors *
128134
*--------------*/
@@ -146,6 +152,15 @@ public ValueStore(File dataDir, boolean forceSync, int valueCacheSize, int value
146152
namespaceIDCache = new ConcurrentCache<>(namespaceIDCacheSize);
147153

148154
setNewRevision();
155+
156+
/*
157+
* Soft failure when a ValueStore is corrupt (i.e., one or more NativeValues cannot be read properly) can be
158+
* enabled using the system property org.eclipse.rdf4j.sail.nativerdf.softFailOnCorruptData (boolean). The
159+
* default behavior is that ValueStore will fail hard with a SailException, whereas softFaileOnCorruptData set
160+
* to true will make ValueStore return instances of CorruptValue if NativeValue cannot be read.
161+
*/
162+
this.softFailOnCorruptData = "true"
163+
.equalsIgnoreCase(System.getProperty("org.eclipse.rdf4j.sail.nativerdf.softFailOnCorruptData"));
149164
}
150165

151166
/*---------*
@@ -526,6 +541,12 @@ private boolean isNamespaceData(byte[] data) {
526541
}
527542

528543
private NativeValue data2value(int id, byte[] data) throws IOException {
544+
if (data.length == 0) {
545+
if (softFailOnCorruptData) {
546+
return new CorruptValue(revision, id);
547+
}
548+
throw new SailException("Empty data array for value with id " + id);
549+
}
529550
switch (data[0]) {
530551
case URI_VALUE:
531552
return data2uri(id, data);
@@ -534,7 +555,10 @@ private NativeValue data2value(int id, byte[] data) throws IOException {
534555
case LITERAL_VALUE:
535556
return data2literal(id, data);
536557
default:
537-
throw new IllegalArgumentException("Invalid type " + data[0] + " for value with id " + id);
558+
if (softFailOnCorruptData) {
559+
return new CorruptValue(revision, id);
560+
}
561+
throw new SailException("Invalid type " + data[0] + " for value with id " + id);
538562
}
539563
}
540564

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2024 Eclipse RDF4J contributors, Aduna, and others.
3+
*
4+
* All rights reserved. This program and the accompanying materials
5+
* are made available under the terms of the Eclipse Distribution License v1.0
6+
* which accompanies this distribution, and is available at
7+
* http://www.eclipse.org/org/documents/edl-v10.php.
8+
*
9+
* SPDX-License-Identifier: BSD-3-Clause
10+
*******************************************************************************/
11+
package org.eclipse.rdf4j.sail.nativerdf.model;
12+
13+
import org.eclipse.rdf4j.sail.nativerdf.ValueStoreRevision;
14+
15+
/**
16+
* CorruptValue is used when a NativeValue cannot be read from the ValueStore and if soft failure is enabled (see
17+
* ValueStore#softFailOnCorruptData).
18+
*
19+
* There is no method isCorruptValue() is it would exist for a "regular" implementation of NativeValue. Since
20+
* CorruptValue is only to be used in exceptional situations, the recommended way of checking for it is using
21+
* "instanceof".
22+
*
23+
* @author Hannes Ebner
24+
*/
25+
public class CorruptValue implements NativeValue {
26+
27+
/*-----------*
28+
* Constants *
29+
*-----------*/
30+
31+
private static final long serialVersionUID = 8829067881854394802L;
32+
33+
/*----------*
34+
* Variables *
35+
*----------*/
36+
37+
private volatile ValueStoreRevision revision;
38+
39+
private volatile int internalID;
40+
41+
/*--------------*
42+
* Constructors *
43+
*--------------*/
44+
45+
public CorruptValue(ValueStoreRevision revision, int internalID) {
46+
setInternalID(internalID, revision);
47+
}
48+
49+
/*---------*
50+
* Methods *
51+
*---------*/
52+
53+
@Override
54+
public void setInternalID(int internalID, ValueStoreRevision revision) {
55+
this.internalID = internalID;
56+
this.revision = revision;
57+
}
58+
59+
@Override
60+
public ValueStoreRevision getValueStoreRevision() {
61+
return revision;
62+
}
63+
64+
@Override
65+
public int getInternalID() {
66+
return internalID;
67+
}
68+
69+
public String stringValue() {
70+
return Integer.toString(internalID);
71+
}
72+
73+
@Override
74+
public boolean equals(Object o) {
75+
if (this == o) {
76+
return true;
77+
}
78+
79+
if (o instanceof CorruptValue && internalID != NativeValue.UNKNOWN_ID) {
80+
CorruptValue otherCorruptValue = (CorruptValue) o;
81+
82+
if (otherCorruptValue.internalID != NativeValue.UNKNOWN_ID && revision.equals(otherCorruptValue.revision)) {
83+
// CorruptValue is from the same revision of the same native store with both IDs set
84+
return internalID == otherCorruptValue.internalID;
85+
}
86+
}
87+
88+
return super.equals(o);
89+
}
90+
91+
}

0 commit comments

Comments
 (0)