From 8206d76a7a5c5a7d9292268a341741737be8b919 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5vard=20M=2E=20Ottestad?= Date: Fri, 12 Dec 2025 08:14:11 +0100 Subject: [PATCH 1/4] GH-0000 Add config toggle for memory-mapped txn status file --- .../rdf4j/model/vocabulary/CONFIG.java | 6 ++ .../rdf4j/sail/nativerdf/NativeSailStore.java | 11 ++-- .../rdf4j/sail/nativerdf/NativeStore.java | 13 +++- .../rdf4j/sail/nativerdf/TripleStore.java | 17 ++++-- .../nativerdf/config/NativeStoreConfig.java | 27 +++++++- .../nativerdf/config/NativeStoreFactory.java | 3 + .../NativeStoreTxnStatusConfigTest.java | 61 +++++++++++++++++++ .../documentation/reference/configuration.md | 2 + 8 files changed, 129 insertions(+), 11 deletions(-) create mode 100644 core/sail/nativerdf/src/test/java/org/eclipse/rdf4j/sail/nativerdf/NativeStoreTxnStatusConfigTest.java diff --git a/core/model-vocabulary/src/main/java/org/eclipse/rdf4j/model/vocabulary/CONFIG.java b/core/model-vocabulary/src/main/java/org/eclipse/rdf4j/model/vocabulary/CONFIG.java index bc9096e4386..52b3fac5744 100644 --- a/core/model-vocabulary/src/main/java/org/eclipse/rdf4j/model/vocabulary/CONFIG.java +++ b/core/model-vocabulary/src/main/java/org/eclipse/rdf4j/model/vocabulary/CONFIG.java @@ -277,6 +277,12 @@ public static final class Native { */ public final static IRI namespaceIDCacheSize = createIRI(NAMESPACE, "native.namespaceIDCacheSize"); + /** + * tag:rdf4j.org,2025:config/native.memoryMappedTxnStatusFile + */ + public final static IRI memoryMappedTxnStatusFile = createIRI(NAMESPACE, + "native.memoryMappedTxnStatusFile"); + // ValueStore WAL configuration properties /** tag:rdf4j.org,2023:config/native.walMaxSegmentBytes */ public final static IRI walMaxSegmentBytes = createIRI(NAMESPACE, "native.walMaxSegmentBytes"); diff --git a/core/sail/nativerdf/src/main/java/org/eclipse/rdf4j/sail/nativerdf/NativeSailStore.java b/core/sail/nativerdf/src/main/java/org/eclipse/rdf4j/sail/nativerdf/NativeSailStore.java index cc84e1a08bb..2bf1954a954 100644 --- a/core/sail/nativerdf/src/main/java/org/eclipse/rdf4j/sail/nativerdf/NativeSailStore.java +++ b/core/sail/nativerdf/src/main/java/org/eclipse/rdf4j/sail/nativerdf/NativeSailStore.java @@ -80,6 +80,7 @@ class NativeSailStore implements SailStore { private final ContextStore contextStore; private final boolean walEnabled; + private final Boolean memoryMappedTxnStatusFileEnabled; /** * A lock to control concurrent access by {@link NativeSailSink} to the TripleStore, ValueStore, and NamespaceStore. @@ -99,7 +100,7 @@ class NativeSailStore implements SailStore { public NativeSailStore(File dataDir, String tripleIndexes) throws IOException, SailException { this(dataDir, tripleIndexes, false, ValueStore.VALUE_CACHE_SIZE, ValueStore.VALUE_ID_CACHE_SIZE, ValueStore.NAMESPACE_CACHE_SIZE, ValueStore.NAMESPACE_ID_CACHE_SIZE, - -1L, -1, -1, null, -1L, -1L, null, false, false, true); + -1L, -1, -1, null, -1L, -1L, null, false, false, true, null); } /** @@ -114,7 +115,7 @@ public NativeSailStore(File dataDir, String tripleIndexes, boolean forceSync, in throws IOException, SailException { this(dataDir, tripleIndexes, forceSync, valueCacheSize, valueIDCacheSize, namespaceCacheSize, namespaceIDCacheSize, walMaxSegmentBytes, walQueueCapacity, walBatchBufferBytes, walSyncPolicy, - walSyncIntervalMillis, walIdlePollIntervalMillis, walDirectoryName, false, false, true); + walSyncIntervalMillis, walIdlePollIntervalMillis, walDirectoryName, false, false, true, null); } public NativeSailStore(File dataDir, String tripleIndexes, boolean forceSync, int valueCacheSize, @@ -122,9 +123,11 @@ public NativeSailStore(File dataDir, String tripleIndexes, boolean forceSync, in int walQueueCapacity, int walBatchBufferBytes, ValueStoreWalConfig.SyncPolicy walSyncPolicy, long walSyncIntervalMillis, long walIdlePollIntervalMillis, String walDirectoryName, - boolean walSyncBootstrapOnOpen, boolean walAutoRecoverOnOpen, boolean walEnabled) + boolean walSyncBootstrapOnOpen, boolean walAutoRecoverOnOpen, boolean walEnabled, + Boolean memoryMappedTxnStatusFileEnabled) throws IOException, SailException { this.walEnabled = walEnabled; + this.memoryMappedTxnStatusFileEnabled = memoryMappedTxnStatusFileEnabled; NamespaceStore createdNamespaceStore = null; ValueStoreWAL createdWal = null; ValueStore createdValueStore = null; @@ -171,7 +174,7 @@ public NativeSailStore(File dataDir, String tripleIndexes, boolean forceSync, in } createdValueStore = new ValueStore(dataDir, forceSync, valueCacheSize, valueIDCacheSize, namespaceCacheSize, namespaceIDCacheSize, createdWal); - createdTripleStore = new TripleStore(dataDir, tripleIndexes, forceSync); + createdTripleStore = new TripleStore(dataDir, tripleIndexes, forceSync, memoryMappedTxnStatusFileEnabled); // Assign fields required by ContextStore before constructing it namespaceStore = createdNamespaceStore; diff --git a/core/sail/nativerdf/src/main/java/org/eclipse/rdf4j/sail/nativerdf/NativeStore.java b/core/sail/nativerdf/src/main/java/org/eclipse/rdf4j/sail/nativerdf/NativeStore.java index 6b3c77d94fa..e5f31939c84 100644 --- a/core/sail/nativerdf/src/main/java/org/eclipse/rdf4j/sail/nativerdf/NativeStore.java +++ b/core/sail/nativerdf/src/main/java/org/eclipse/rdf4j/sail/nativerdf/NativeStore.java @@ -144,6 +144,8 @@ protected SailStore createSailStore(File dataDir) throws IOException, SailExcept private volatile int namespaceIDCacheSize = ValueStore.NAMESPACE_ID_CACHE_SIZE; + private volatile Boolean memoryMappedTxnStatusFileEnabled; + private SailStore store; // used to decide if store is writable, is true if the store was writable during initialization @@ -276,6 +278,14 @@ public void setNamespaceIDCacheSize(int namespaceIDCacheSize) { this.namespaceIDCacheSize = namespaceIDCacheSize; } + public Boolean getMemoryMappedTxnStatusFileEnabled() { + return memoryMappedTxnStatusFileEnabled; + } + + public void setMemoryMappedTxnStatusFileEnabled(Boolean memoryMappedTxnStatusFileEnabled) { + this.memoryMappedTxnStatusFileEnabled = memoryMappedTxnStatusFileEnabled; + } + @Experimental public void setWalMaxSegmentBytes(long walMaxSegmentBytes) { this.walMaxSegmentBytes = walMaxSegmentBytes; @@ -492,7 +502,8 @@ protected void initializeInternal() throws SailException { walDirectoryName, walSyncBootstrapOnOpen, walAutoRecoverOnOpen, - walEnabled); + walEnabled, + memoryMappedTxnStatusFileEnabled); this.store = new SnapshotSailStore(mainStore, MemoryOverflowIntoNativeStore::new) { @Override diff --git a/core/sail/nativerdf/src/main/java/org/eclipse/rdf4j/sail/nativerdf/TripleStore.java b/core/sail/nativerdf/src/main/java/org/eclipse/rdf4j/sail/nativerdf/TripleStore.java index 88e60a3eaa4..136e72644ad 100644 --- a/core/sail/nativerdf/src/main/java/org/eclipse/rdf4j/sail/nativerdf/TripleStore.java +++ b/core/sail/nativerdf/src/main/java/org/eclipse/rdf4j/sail/nativerdf/TripleStore.java @@ -168,13 +168,18 @@ class TripleStore implements Closeable { *--------------*/ public TripleStore(File dir, String indexSpecStr) throws IOException, SailException { - this(dir, indexSpecStr, false); + this(dir, indexSpecStr, false, null); } public TripleStore(File dir, String indexSpecStr, boolean forceSync) throws IOException, SailException { + this(dir, indexSpecStr, forceSync, null); + } + + public TripleStore(File dir, String indexSpecStr, boolean forceSync, Boolean memoryMappedTxnStatusFileEnabled) + throws IOException, SailException { this.dir = dir; this.forceSync = forceSync; - this.txnStatusFile = createTxnStatusFile(dir); + this.txnStatusFile = createTxnStatusFile(dir, memoryMappedTxnStatusFileEnabled); File propFile = new File(dir, PROPERTIES_FILE); @@ -229,8 +234,12 @@ public TripleStore(File dir, String indexSpecStr, boolean forceSync) throws IOEx } } - private static TxnStatusFile createTxnStatusFile(File dir) throws IOException { - if (Boolean.getBoolean(MEMORY_MAPPED_TXN_STATUS_FILE_ENABLED_PROP)) { + private static TxnStatusFile createTxnStatusFile(File dir, Boolean memoryMappedTxnStatusFileEnabled) + throws IOException { + boolean enabled = memoryMappedTxnStatusFileEnabled != null + ? memoryMappedTxnStatusFileEnabled + : Boolean.getBoolean(MEMORY_MAPPED_TXN_STATUS_FILE_ENABLED_PROP); + if (enabled) { return new MemoryMappedTxnStatusFile(dir); } return new TxnStatusFile(dir); diff --git a/core/sail/nativerdf/src/main/java/org/eclipse/rdf4j/sail/nativerdf/config/NativeStoreConfig.java b/core/sail/nativerdf/src/main/java/org/eclipse/rdf4j/sail/nativerdf/config/NativeStoreConfig.java index b757cf0e3f8..2e9cac53097 100644 --- a/core/sail/nativerdf/src/main/java/org/eclipse/rdf4j/sail/nativerdf/config/NativeStoreConfig.java +++ b/core/sail/nativerdf/src/main/java/org/eclipse/rdf4j/sail/nativerdf/config/NativeStoreConfig.java @@ -37,6 +37,7 @@ public class NativeStoreConfig extends BaseSailConfig { private int valueIDCacheSize = -1; private int namespaceCacheSize = -1; private int namespaceIDCacheSize = -1; + private Boolean memoryMappedTxnStatusFileEnabled; // WAL: expose max segment bytes via config (optional) private long walMaxSegmentBytes = -1L; @@ -124,6 +125,14 @@ public void setNamespaceIDCacheSize(int namespaceIDCacheSize) { this.namespaceIDCacheSize = namespaceIDCacheSize; } + public Boolean getMemoryMappedTxnStatusFileEnabled() { + return memoryMappedTxnStatusFileEnabled; + } + + public void setMemoryMappedTxnStatusFileEnabled(Boolean memoryMappedTxnStatusFileEnabled) { + this.memoryMappedTxnStatusFileEnabled = memoryMappedTxnStatusFileEnabled; + } + public long getWalMaxSegmentBytes() { return walMaxSegmentBytes; } @@ -231,6 +240,10 @@ public Resource export(Model m) { if (namespaceIDCacheSize >= 0) { m.add(implNode, CONFIG.Native.namespaceIDCacheSize, literal(namespaceIDCacheSize)); } + if (memoryMappedTxnStatusFileEnabled != null) { + m.add(implNode, CONFIG.Native.memoryMappedTxnStatusFile, + literal(memoryMappedTxnStatusFileEnabled)); + } // WAL configuration properties if (walMaxSegmentBytes >= 0) { m.add(implNode, CONFIG.Native.walMaxSegmentBytes, literal(walMaxSegmentBytes)); @@ -347,8 +360,8 @@ public void parse(Model m, Resource implNode) throws SailConfigException { } }); - Configurations.getLiteralValue(m, implNode, CONFIG.Native.namespaceIDCacheSize, NAMESPACE_ID_CACHE_SIZE) - .ifPresent(lit -> { + Configurations.getLiteralValue(m, implNode, CONFIG.Native.namespaceIDCacheSize, + NAMESPACE_ID_CACHE_SIZE).ifPresent(lit -> { try { setNamespaceIDCacheSize(lit.intValue()); } catch (NumberFormatException e) { @@ -358,6 +371,16 @@ public void parse(Model m, Resource implNode) throws SailConfigException { } }); + Configurations.getLiteralValue(m, implNode, CONFIG.Native.memoryMappedTxnStatusFile) + .ifPresent(lit -> { + try { + setMemoryMappedTxnStatusFileEnabled(lit.booleanValue()); + } catch (IllegalArgumentException e) { + throw new SailConfigException("Boolean value required for " + + CONFIG.Native.memoryMappedTxnStatusFile + " property, found " + lit); + } + }); + // WAL configuration properties Configurations.getLiteralValue(m, implNode, CONFIG.Native.walMaxSegmentBytes) .ifPresent(lit -> { diff --git a/core/sail/nativerdf/src/main/java/org/eclipse/rdf4j/sail/nativerdf/config/NativeStoreFactory.java b/core/sail/nativerdf/src/main/java/org/eclipse/rdf4j/sail/nativerdf/config/NativeStoreFactory.java index 26c858df305..1a494fb9c18 100644 --- a/core/sail/nativerdf/src/main/java/org/eclipse/rdf4j/sail/nativerdf/config/NativeStoreFactory.java +++ b/core/sail/nativerdf/src/main/java/org/eclipse/rdf4j/sail/nativerdf/config/NativeStoreFactory.java @@ -72,6 +72,9 @@ public Sail getSail(SailImplConfig config) throws SailConfigException { if (nativeConfig.getNamespaceIDCacheSize() >= 0) { nativeStore.setNamespaceIDCacheSize(nativeConfig.getNamespaceIDCacheSize()); } + if (nativeConfig.getMemoryMappedTxnStatusFileEnabled() != null) { + nativeStore.setMemoryMappedTxnStatusFileEnabled(nativeConfig.getMemoryMappedTxnStatusFileEnabled()); + } if (nativeConfig.getIterationCacheSyncThreshold() > 0) { nativeStore.setIterationCacheSyncThreshold(nativeConfig.getIterationCacheSyncThreshold()); } diff --git a/core/sail/nativerdf/src/test/java/org/eclipse/rdf4j/sail/nativerdf/NativeStoreTxnStatusConfigTest.java b/core/sail/nativerdf/src/test/java/org/eclipse/rdf4j/sail/nativerdf/NativeStoreTxnStatusConfigTest.java new file mode 100644 index 00000000000..1d2e39b5929 --- /dev/null +++ b/core/sail/nativerdf/src/test/java/org/eclipse/rdf4j/sail/nativerdf/NativeStoreTxnStatusConfigTest.java @@ -0,0 +1,61 @@ +/******************************************************************************* + * 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; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.File; + +import org.eclipse.rdf4j.model.IRI; +import org.eclipse.rdf4j.model.ValueFactory; +import org.eclipse.rdf4j.model.impl.SimpleValueFactory; +import org.eclipse.rdf4j.repository.Repository; +import org.eclipse.rdf4j.repository.RepositoryConnection; +import org.eclipse.rdf4j.repository.sail.SailRepository; +import org.eclipse.rdf4j.sail.nativerdf.config.NativeStoreConfig; +import org.eclipse.rdf4j.sail.nativerdf.config.NativeStoreFactory; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +class NativeStoreTxnStatusConfigTest { + + @TempDir + File dataDir; + + @AfterEach + void clearSystemProperty() { + System.clearProperty("org.eclipse.rdf4j.sail.nativerdf.MemoryMappedTxnStatusFile.enabled"); + } + + @Test + void configEnablesMemoryMappedTxnStatusFile() throws Exception { + NativeStoreConfig cfg = new NativeStoreConfig("spoc"); + cfg.setMemoryMappedTxnStatusFileEnabled(true); + + NativeStoreFactory factory = new NativeStoreFactory(); + NativeStore sail = (NativeStore) factory.getSail(cfg); + sail.setDataDir(dataDir); + + Repository repo = new SailRepository(sail); + repo.init(); + try (RepositoryConnection conn = repo.getConnection()) { + ValueFactory vf = SimpleValueFactory.getInstance(); + IRI p = vf.createIRI("http://example.com/p"); + conn.add(vf.createIRI("http://example.com/s"), p, vf.createLiteral("o")); + } + repo.shutDown(); + + File txnStatusFile = new File(dataDir, TxnStatusFile.FILE_NAME); + assertThat(txnStatusFile).exists(); + assertThat(txnStatusFile.length()).isEqualTo(1L); + } +} diff --git a/site/content/documentation/reference/configuration.md b/site/content/documentation/reference/configuration.md index 93e0e121508..70ae5ee92da 100644 --- a/site/content/documentation/reference/configuration.md +++ b/site/content/documentation/reference/configuration.md @@ -251,6 +251,8 @@ Creating more indexes potentially speeds up querying (a lot), but also adds over The native store automatically creates/drops indexes upon (re)initialization, so the parameter can be adjusted and upon the first refresh of the configuration the native store will change its indexing strategy, without loss of data. +Set `config:native.memoryMappedTxnStatusFile` to `true` to enable the experimental memory-mapped transaction status file. When unset, the store falls back to the legacy file implementation and respects the JVM system property `-Dorg.eclipse.rdf4j.sail.nativerdf.MemoryMappedTxnStatusFile.enabled`. + ##### Example configuration ```turtle From db7f5cf7471e501e7af16153fb7f786f3639f067 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5vard=20M=2E=20Ottestad?= Date: Fri, 12 Dec 2025 10:19:49 +0100 Subject: [PATCH 2/4] GH-0000 Verify memory-mapped txn status file via reflection --- .../NativeStoreTxnStatusConfigTest.java | 42 ++++++++++++------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/core/sail/nativerdf/src/test/java/org/eclipse/rdf4j/sail/nativerdf/NativeStoreTxnStatusConfigTest.java b/core/sail/nativerdf/src/test/java/org/eclipse/rdf4j/sail/nativerdf/NativeStoreTxnStatusConfigTest.java index 1d2e39b5929..4e46d7fd9f0 100644 --- a/core/sail/nativerdf/src/test/java/org/eclipse/rdf4j/sail/nativerdf/NativeStoreTxnStatusConfigTest.java +++ b/core/sail/nativerdf/src/test/java/org/eclipse/rdf4j/sail/nativerdf/NativeStoreTxnStatusConfigTest.java @@ -13,6 +13,7 @@ import static org.assertj.core.api.Assertions.assertThat; import java.io.File; +import java.lang.reflect.Field; import org.eclipse.rdf4j.model.IRI; import org.eclipse.rdf4j.model.ValueFactory; @@ -42,20 +43,33 @@ void configEnablesMemoryMappedTxnStatusFile() throws Exception { cfg.setMemoryMappedTxnStatusFileEnabled(true); NativeStoreFactory factory = new NativeStoreFactory(); - NativeStore sail = (NativeStore) factory.getSail(cfg); - sail.setDataDir(dataDir); - - Repository repo = new SailRepository(sail); - repo.init(); - try (RepositoryConnection conn = repo.getConnection()) { - ValueFactory vf = SimpleValueFactory.getInstance(); - IRI p = vf.createIRI("http://example.com/p"); - conn.add(vf.createIRI("http://example.com/s"), p, vf.createLiteral("o")); - } + NativeStore sail = (NativeStore) factory.getSail(cfg); + sail.setDataDir(dataDir); + + Repository repo = new SailRepository(sail); + repo.init(); + assertThat(extractTxnStatusFile(sail)).isInstanceOf(MemoryMappedTxnStatusFile.class); + try (RepositoryConnection conn = repo.getConnection()) { + ValueFactory vf = SimpleValueFactory.getInstance(); + IRI p = vf.createIRI("http://example.com/p"); + conn.add(vf.createIRI("http://example.com/s"), p, vf.createLiteral("o")); + } repo.shutDown(); - File txnStatusFile = new File(dataDir, TxnStatusFile.FILE_NAME); - assertThat(txnStatusFile).exists(); - assertThat(txnStatusFile.length()).isEqualTo(1L); - } + File txnStatusFile = new File(dataDir, TxnStatusFile.FILE_NAME); + assertThat(txnStatusFile).exists(); + assertThat(txnStatusFile.length()).isEqualTo(1L); + } + + private TxnStatusFile extractTxnStatusFile(NativeStore sail) throws Exception { + NativeSailStore store = (NativeSailStore) sail.getSailStore(); + + Field tripleStoreField = NativeSailStore.class.getDeclaredField("tripleStore"); + tripleStoreField.setAccessible(true); + TripleStore tripleStore = (TripleStore) tripleStoreField.get(store); + + Field txnStatusFileField = TripleStore.class.getDeclaredField("txnStatusFile"); + txnStatusFileField.setAccessible(true); + return (TxnStatusFile) txnStatusFileField.get(tripleStore); + } } From d1284526810f07c70a03a197e88dfda9ff4a8c5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5vard=20M=2E=20Ottestad?= Date: Fri, 12 Dec 2025 22:54:35 +0100 Subject: [PATCH 3/4] GH-0000 Remove system property toggle for memory mapped txn status file --- .../rdf4j/sail/nativerdf/TripleStore.java | 26 ++++------ .../MemoryMappedTxnStatusFileConfigTest.java | 47 ++++++++++--------- .../NativeStoreTxnStatusConfigTest.java | 10 +--- .../documentation/reference/configuration.md | 2 +- 4 files changed, 36 insertions(+), 49 deletions(-) diff --git a/core/sail/nativerdf/src/main/java/org/eclipse/rdf4j/sail/nativerdf/TripleStore.java b/core/sail/nativerdf/src/main/java/org/eclipse/rdf4j/sail/nativerdf/TripleStore.java index 136e72644ad..f01b8f6621f 100644 --- a/core/sail/nativerdf/src/main/java/org/eclipse/rdf4j/sail/nativerdf/TripleStore.java +++ b/core/sail/nativerdf/src/main/java/org/eclipse/rdf4j/sail/nativerdf/TripleStore.java @@ -80,15 +80,9 @@ class TripleStore implements Closeable { */ private static final String INDEXES_KEY = "triple-indexes"; - /** - * System property that enables the experimental {@link MemoryMappedTxnStatusFile} implementation instead of the - * default {@link TxnStatusFile}. - */ - private static final String MEMORY_MAPPED_TXN_STATUS_FILE_ENABLED_PROP = "org.eclipse.rdf4j.sail.nativerdf.MemoryMappedTxnStatusFile.enabled"; - - /** - * The version number for the current triple store. - *
    + /** + * The version number for the current triple store. + *
      *
    • version 0: The first version which used a single spo-index. This version did not have a properties file yet. *
    • version 1: Introduces configurable triple indexes and the properties file. *
    • version 10: Introduces a context field, essentially making this a quad store. @@ -234,14 +228,12 @@ public TripleStore(File dir, String indexSpecStr, boolean forceSync, Boolean mem } } - private static TxnStatusFile createTxnStatusFile(File dir, Boolean memoryMappedTxnStatusFileEnabled) - throws IOException { - boolean enabled = memoryMappedTxnStatusFileEnabled != null - ? memoryMappedTxnStatusFileEnabled - : Boolean.getBoolean(MEMORY_MAPPED_TXN_STATUS_FILE_ENABLED_PROP); - if (enabled) { - return new MemoryMappedTxnStatusFile(dir); - } + private static TxnStatusFile createTxnStatusFile(File dir, Boolean memoryMappedTxnStatusFileEnabled) + throws IOException { + boolean enabled = Boolean.TRUE.equals(memoryMappedTxnStatusFileEnabled); + if (enabled) { + return new MemoryMappedTxnStatusFile(dir); + } return new TxnStatusFile(dir); } diff --git a/core/sail/nativerdf/src/test/java/org/eclipse/rdf4j/sail/nativerdf/MemoryMappedTxnStatusFileConfigTest.java b/core/sail/nativerdf/src/test/java/org/eclipse/rdf4j/sail/nativerdf/MemoryMappedTxnStatusFileConfigTest.java index edc9441c130..b175c2cf54a 100644 --- a/core/sail/nativerdf/src/test/java/org/eclipse/rdf4j/sail/nativerdf/MemoryMappedTxnStatusFileConfigTest.java +++ b/core/sail/nativerdf/src/test/java/org/eclipse/rdf4j/sail/nativerdf/MemoryMappedTxnStatusFileConfigTest.java @@ -20,23 +20,24 @@ import org.junit.jupiter.api.io.TempDir; /** - * Verifies that the implementation used for the transaction status file can be controlled via a system property. + * Verifies that the implementation used for the transaction status file is controlled through configuration rather than a + * JVM system property. */ public class MemoryMappedTxnStatusFileConfigTest { - private static final String MEMORY_MAPPED_ENABLED_PROP = "org.eclipse.rdf4j.sail.nativerdf.MemoryMappedTxnStatusFile.enabled"; + private static final String MEMORY_MAPPED_ENABLED_PROP = "org.eclipse.rdf4j.sail.nativerdf.MemoryMappedTxnStatusFile.enabled"; - @TempDir - File dataDir; + @TempDir + File dataDir; - @AfterEach - public void clearProperty() { - System.clearProperty(MEMORY_MAPPED_ENABLED_PROP); - } + @AfterEach + public void clearProperty() { + System.clearProperty(MEMORY_MAPPED_ENABLED_PROP); + } - @Test - public void defaultUsesNioTxnStatusFile() throws Exception { - TripleStore tripleStore = new TripleStore(dataDir, "spoc"); + @Test + public void defaultUsesNioTxnStatusFile() throws Exception { + TripleStore tripleStore = new TripleStore(dataDir, "spoc"); try { tripleStore.startTransaction(); tripleStore.storeTriple(1, 2, 3, 4); @@ -52,21 +53,21 @@ public void defaultUsesNioTxnStatusFile() throws Exception { } @Test - public void memoryMappedEnabledUsesFixedSizeFile() throws Exception { - System.setProperty(MEMORY_MAPPED_ENABLED_PROP, "true"); + public void systemPropertyIsIgnored() throws Exception { + System.setProperty(MEMORY_MAPPED_ENABLED_PROP, "true"); - TripleStore tripleStore = new TripleStore(dataDir, "spoc"); - try { - tripleStore.startTransaction(); - tripleStore.storeTriple(1, 2, 3, 4); + TripleStore tripleStore = new TripleStore(dataDir, "spoc"); + try { + tripleStore.startTransaction(); + tripleStore.storeTriple(1, 2, 3, 4); tripleStore.commit(); } finally { tripleStore.close(); - } + } - File txnStatusFile = new File(dataDir, TxnStatusFile.FILE_NAME); - assertTrue(txnStatusFile.exists(), "Transaction status file should exist"); - assertEquals(1L, txnStatusFile.length(), - "Memory-mapped TxnStatusFile keeps a single status byte on disk for NONE status"); - } + File txnStatusFile = new File(dataDir, TxnStatusFile.FILE_NAME); + assertTrue(txnStatusFile.exists(), "Transaction status file should exist"); + assertEquals(0L, txnStatusFile.length(), + "System property does not switch to memory-mapped TxnStatusFile"); + } } diff --git a/core/sail/nativerdf/src/test/java/org/eclipse/rdf4j/sail/nativerdf/NativeStoreTxnStatusConfigTest.java b/core/sail/nativerdf/src/test/java/org/eclipse/rdf4j/sail/nativerdf/NativeStoreTxnStatusConfigTest.java index 4e46d7fd9f0..60129efb3c6 100644 --- a/core/sail/nativerdf/src/test/java/org/eclipse/rdf4j/sail/nativerdf/NativeStoreTxnStatusConfigTest.java +++ b/core/sail/nativerdf/src/test/java/org/eclipse/rdf4j/sail/nativerdf/NativeStoreTxnStatusConfigTest.java @@ -23,7 +23,6 @@ import org.eclipse.rdf4j.repository.sail.SailRepository; import org.eclipse.rdf4j.sail.nativerdf.config.NativeStoreConfig; import org.eclipse.rdf4j.sail.nativerdf.config.NativeStoreFactory; -import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; @@ -32,13 +31,8 @@ class NativeStoreTxnStatusConfigTest { @TempDir File dataDir; - @AfterEach - void clearSystemProperty() { - System.clearProperty("org.eclipse.rdf4j.sail.nativerdf.MemoryMappedTxnStatusFile.enabled"); - } - - @Test - void configEnablesMemoryMappedTxnStatusFile() throws Exception { + @Test + void configEnablesMemoryMappedTxnStatusFile() throws Exception { NativeStoreConfig cfg = new NativeStoreConfig("spoc"); cfg.setMemoryMappedTxnStatusFileEnabled(true); diff --git a/site/content/documentation/reference/configuration.md b/site/content/documentation/reference/configuration.md index 70ae5ee92da..687b345051d 100644 --- a/site/content/documentation/reference/configuration.md +++ b/site/content/documentation/reference/configuration.md @@ -251,7 +251,7 @@ Creating more indexes potentially speeds up querying (a lot), but also adds over The native store automatically creates/drops indexes upon (re)initialization, so the parameter can be adjusted and upon the first refresh of the configuration the native store will change its indexing strategy, without loss of data. -Set `config:native.memoryMappedTxnStatusFile` to `true` to enable the experimental memory-mapped transaction status file. When unset, the store falls back to the legacy file implementation and respects the JVM system property `-Dorg.eclipse.rdf4j.sail.nativerdf.MemoryMappedTxnStatusFile.enabled`. +Set `config:native.memoryMappedTxnStatusFile` to `true` to enable the experimental memory-mapped transaction status file. When unset, the store falls back to the legacy file implementation. ##### Example configuration From 7cf3cd6440b80d6663fd8a65d28cb470ad03a023 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ha=CC=8Avard=20Ottestad?= Date: Wed, 17 Dec 2025 12:50:47 +0100 Subject: [PATCH 4/4] formatting --- .../rdf4j/sail/nativerdf/TripleStore.java | 18 +++---- .../MemoryMappedTxnStatusFileConfigTest.java | 48 +++++++++--------- .../NativeStoreTxnStatusConfigTest.java | 50 +++++++++---------- 3 files changed, 58 insertions(+), 58 deletions(-) diff --git a/core/sail/nativerdf/src/main/java/org/eclipse/rdf4j/sail/nativerdf/TripleStore.java b/core/sail/nativerdf/src/main/java/org/eclipse/rdf4j/sail/nativerdf/TripleStore.java index f01b8f6621f..ccb1b4c4943 100644 --- a/core/sail/nativerdf/src/main/java/org/eclipse/rdf4j/sail/nativerdf/TripleStore.java +++ b/core/sail/nativerdf/src/main/java/org/eclipse/rdf4j/sail/nativerdf/TripleStore.java @@ -80,9 +80,9 @@ class TripleStore implements Closeable { */ private static final String INDEXES_KEY = "triple-indexes"; - /** - * The version number for the current triple store. - *
        + /** + * The version number for the current triple store. + *
          *
        • version 0: The first version which used a single spo-index. This version did not have a properties file yet. *
        • version 1: Introduces configurable triple indexes and the properties file. *
        • version 10: Introduces a context field, essentially making this a quad store. @@ -228,12 +228,12 @@ public TripleStore(File dir, String indexSpecStr, boolean forceSync, Boolean mem } } - private static TxnStatusFile createTxnStatusFile(File dir, Boolean memoryMappedTxnStatusFileEnabled) - throws IOException { - boolean enabled = Boolean.TRUE.equals(memoryMappedTxnStatusFileEnabled); - if (enabled) { - return new MemoryMappedTxnStatusFile(dir); - } + private static TxnStatusFile createTxnStatusFile(File dir, Boolean memoryMappedTxnStatusFileEnabled) + throws IOException { + boolean enabled = Boolean.TRUE.equals(memoryMappedTxnStatusFileEnabled); + if (enabled) { + return new MemoryMappedTxnStatusFile(dir); + } return new TxnStatusFile(dir); } diff --git a/core/sail/nativerdf/src/test/java/org/eclipse/rdf4j/sail/nativerdf/MemoryMappedTxnStatusFileConfigTest.java b/core/sail/nativerdf/src/test/java/org/eclipse/rdf4j/sail/nativerdf/MemoryMappedTxnStatusFileConfigTest.java index b175c2cf54a..8370aec0f70 100644 --- a/core/sail/nativerdf/src/test/java/org/eclipse/rdf4j/sail/nativerdf/MemoryMappedTxnStatusFileConfigTest.java +++ b/core/sail/nativerdf/src/test/java/org/eclipse/rdf4j/sail/nativerdf/MemoryMappedTxnStatusFileConfigTest.java @@ -20,24 +20,24 @@ import org.junit.jupiter.api.io.TempDir; /** - * Verifies that the implementation used for the transaction status file is controlled through configuration rather than a - * JVM system property. + * Verifies that the implementation used for the transaction status file is controlled through configuration rather than + * a JVM system property. */ public class MemoryMappedTxnStatusFileConfigTest { - private static final String MEMORY_MAPPED_ENABLED_PROP = "org.eclipse.rdf4j.sail.nativerdf.MemoryMappedTxnStatusFile.enabled"; + private static final String MEMORY_MAPPED_ENABLED_PROP = "org.eclipse.rdf4j.sail.nativerdf.MemoryMappedTxnStatusFile.enabled"; - @TempDir - File dataDir; + @TempDir + File dataDir; - @AfterEach - public void clearProperty() { - System.clearProperty(MEMORY_MAPPED_ENABLED_PROP); - } + @AfterEach + public void clearProperty() { + System.clearProperty(MEMORY_MAPPED_ENABLED_PROP); + } - @Test - public void defaultUsesNioTxnStatusFile() throws Exception { - TripleStore tripleStore = new TripleStore(dataDir, "spoc"); + @Test + public void defaultUsesNioTxnStatusFile() throws Exception { + TripleStore tripleStore = new TripleStore(dataDir, "spoc"); try { tripleStore.startTransaction(); tripleStore.storeTriple(1, 2, 3, 4); @@ -53,21 +53,21 @@ public void defaultUsesNioTxnStatusFile() throws Exception { } @Test - public void systemPropertyIsIgnored() throws Exception { - System.setProperty(MEMORY_MAPPED_ENABLED_PROP, "true"); + public void systemPropertyIsIgnored() throws Exception { + System.setProperty(MEMORY_MAPPED_ENABLED_PROP, "true"); - TripleStore tripleStore = new TripleStore(dataDir, "spoc"); - try { - tripleStore.startTransaction(); - tripleStore.storeTriple(1, 2, 3, 4); + TripleStore tripleStore = new TripleStore(dataDir, "spoc"); + try { + tripleStore.startTransaction(); + tripleStore.storeTriple(1, 2, 3, 4); tripleStore.commit(); } finally { tripleStore.close(); - } + } - File txnStatusFile = new File(dataDir, TxnStatusFile.FILE_NAME); - assertTrue(txnStatusFile.exists(), "Transaction status file should exist"); - assertEquals(0L, txnStatusFile.length(), - "System property does not switch to memory-mapped TxnStatusFile"); - } + File txnStatusFile = new File(dataDir, TxnStatusFile.FILE_NAME); + assertTrue(txnStatusFile.exists(), "Transaction status file should exist"); + assertEquals(0L, txnStatusFile.length(), + "System property does not switch to memory-mapped TxnStatusFile"); + } } diff --git a/core/sail/nativerdf/src/test/java/org/eclipse/rdf4j/sail/nativerdf/NativeStoreTxnStatusConfigTest.java b/core/sail/nativerdf/src/test/java/org/eclipse/rdf4j/sail/nativerdf/NativeStoreTxnStatusConfigTest.java index 60129efb3c6..c3f8641721e 100644 --- a/core/sail/nativerdf/src/test/java/org/eclipse/rdf4j/sail/nativerdf/NativeStoreTxnStatusConfigTest.java +++ b/core/sail/nativerdf/src/test/java/org/eclipse/rdf4j/sail/nativerdf/NativeStoreTxnStatusConfigTest.java @@ -31,39 +31,39 @@ class NativeStoreTxnStatusConfigTest { @TempDir File dataDir; - @Test - void configEnablesMemoryMappedTxnStatusFile() throws Exception { + @Test + void configEnablesMemoryMappedTxnStatusFile() throws Exception { NativeStoreConfig cfg = new NativeStoreConfig("spoc"); cfg.setMemoryMappedTxnStatusFileEnabled(true); NativeStoreFactory factory = new NativeStoreFactory(); - NativeStore sail = (NativeStore) factory.getSail(cfg); - sail.setDataDir(dataDir); + NativeStore sail = (NativeStore) factory.getSail(cfg); + sail.setDataDir(dataDir); - Repository repo = new SailRepository(sail); - repo.init(); - assertThat(extractTxnStatusFile(sail)).isInstanceOf(MemoryMappedTxnStatusFile.class); - try (RepositoryConnection conn = repo.getConnection()) { - ValueFactory vf = SimpleValueFactory.getInstance(); - IRI p = vf.createIRI("http://example.com/p"); - conn.add(vf.createIRI("http://example.com/s"), p, vf.createLiteral("o")); - } + Repository repo = new SailRepository(sail); + repo.init(); + assertThat(extractTxnStatusFile(sail)).isInstanceOf(MemoryMappedTxnStatusFile.class); + try (RepositoryConnection conn = repo.getConnection()) { + ValueFactory vf = SimpleValueFactory.getInstance(); + IRI p = vf.createIRI("http://example.com/p"); + conn.add(vf.createIRI("http://example.com/s"), p, vf.createLiteral("o")); + } repo.shutDown(); - File txnStatusFile = new File(dataDir, TxnStatusFile.FILE_NAME); - assertThat(txnStatusFile).exists(); - assertThat(txnStatusFile.length()).isEqualTo(1L); - } + File txnStatusFile = new File(dataDir, TxnStatusFile.FILE_NAME); + assertThat(txnStatusFile).exists(); + assertThat(txnStatusFile.length()).isEqualTo(1L); + } - private TxnStatusFile extractTxnStatusFile(NativeStore sail) throws Exception { - NativeSailStore store = (NativeSailStore) sail.getSailStore(); + private TxnStatusFile extractTxnStatusFile(NativeStore sail) throws Exception { + NativeSailStore store = (NativeSailStore) sail.getSailStore(); - Field tripleStoreField = NativeSailStore.class.getDeclaredField("tripleStore"); - tripleStoreField.setAccessible(true); - TripleStore tripleStore = (TripleStore) tripleStoreField.get(store); + Field tripleStoreField = NativeSailStore.class.getDeclaredField("tripleStore"); + tripleStoreField.setAccessible(true); + TripleStore tripleStore = (TripleStore) tripleStoreField.get(store); - Field txnStatusFileField = TripleStore.class.getDeclaredField("txnStatusFile"); - txnStatusFileField.setAccessible(true); - return (TxnStatusFile) txnStatusFileField.get(tripleStore); - } + Field txnStatusFileField = TripleStore.class.getDeclaredField("txnStatusFile"); + txnStatusFileField.setAccessible(true); + return (TxnStatusFile) txnStatusFileField.get(tripleStore); + } }