From d0031a215ede776c02f654bbd997b8eec151426b Mon Sep 17 00:00:00 2001 From: shah Date: Thu, 23 Jul 2026 21:52:04 +0200 Subject: [PATCH 1/4] GH-4084: Fix ByteBufferPool sizing in Fuseki's Jetty Server Fuseki builds its Jetty Server with new Server(threadPool), which wires in a default ArrayByteBufferPool capped at 64KB. Fuseki configures a 5MB outputBufferSize, so output buffers exceed the pool's max capacity and are discarded on release instead of reused, causing continuous DirectMemory churn under load. Fixed in JettyServer.jettyServer(...), the shared factory both JettyHttps.java and FusekiServer.java call, so neither file needs changes. Constructs an ArrayByteBufferPool sized to FusekiSystemConstants.jettyOutputBufferSize and passes it into Server(ThreadPool, Scheduler, ByteBufferPool) at construction time, since adding the pool via addBean() afterward has no effect (the default pool is already wired in during construction). --- .../org/apache/jena/fuseki/main/JettyServer.java | 12 +++++++++++- .../jena/fuseki/main/TestFusekiServerBuild.java | 11 +++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/JettyServer.java b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/JettyServer.java index e3d7a8e431e..d263f227bdc 100644 --- a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/JettyServer.java +++ b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/JettyServer.java @@ -50,6 +50,8 @@ import org.eclipse.jetty.ee11.servlet.ServletContextHandler; import org.eclipse.jetty.ee11.servlet.ServletHolder; import org.eclipse.jetty.http.MimeTypes; +import org.eclipse.jetty.io.ArrayByteBufferPool; +import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.security.SecurityHandler; import org.eclipse.jetty.server.*; import org.eclipse.jetty.server.handler.ErrorHandler; @@ -473,10 +475,18 @@ public static Server jettyServer(int minThreads, int maxThreads) { maxThreads = Math.max(minThreads, maxThreads); // Args reversed: Jetty uses (max,min) threadPool = new QueuedThreadPool(maxThreads, minThreads); - Server server = new Server(threadPool); + // Server(ThreadPool) alone installs a default 64KB ArrayByteBufferPool; pass ours explicitly. + Server server = new Server(threadPool, null, newByteBufferPool()); return server; } + /** ByteBufferPool sized so its maximum pooled buffer capacity is at least + * {@link FusekiSystemConstants#jettyOutputBufferSize}. */ + private static ByteBufferPool newByteBufferPool() { + int maxCapacity = FusekiSystemConstants.jettyOutputBufferSize; + return new ArrayByteBufferPool(0, 2048, maxCapacity, -1, -1, -1); + } + private static void serverAddConnectors(Server server, int port, boolean loopback) { HttpConfiguration httpConnectionFactory = JettyLib.httpConfiguration(); HttpConnectionFactory f1 = new HttpConnectionFactory(httpConnectionFactory); diff --git a/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/TestFusekiServerBuild.java b/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/TestFusekiServerBuild.java index a254704c80e..24cc9e325fb 100644 --- a/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/TestFusekiServerBuild.java +++ b/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/TestFusekiServerBuild.java @@ -54,8 +54,11 @@ import org.apache.jena.sparql.exec.http.GSP; import org.apache.jena.sparql.exec.http.QueryExecHTTP; import org.apache.jena.sparql.sse.SSE; +import org.apache.jena.fuseki.main.sys.FusekiSystemConstants; import org.apache.jena.system.Txn; import org.apache.jena.update.UpdateExecution; +import org.eclipse.jetty.io.ArrayByteBufferPool; +import org.eclipse.jetty.server.Server; import org.junit.jupiter.api.Test; import org.slf4j.Logger; @@ -85,6 +88,14 @@ public class TestFusekiServerBuild { } finally { server.stop(); } } + @Test public void fuseki_build_byte_buffer_pool() { + FusekiServer server = FusekiServer.create().port(0).build(); + Server jettyServer = server.getJettyServer(); + ArrayByteBufferPool pool = jettyServer.getBean(ArrayByteBufferPool.class); + assertNotNull(pool); + assertTrue(pool.getMaxCapacity() >= FusekiSystemConstants.jettyOutputBufferSize); + } + // The port in "testing/jetty.xml" is 1077 @Test public void fuseki_ext_jetty_xml_1() { From 4d595c096bf3c0406e15409f16de7c82c6e2e194 Mon Sep 17 00:00:00 2001 From: divyank sameer shah <49211019+divyankshah@users.noreply.github.com> Date: Wed, 29 Jul 2026 15:49:33 +0200 Subject: [PATCH 2/4] Update explicit (Scheduler) cast to null for readability Co-authored-by: Andy Seaborne --- .../src/main/java/org/apache/jena/fuseki/main/JettyServer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/JettyServer.java b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/JettyServer.java index d263f227bdc..d2632d397dd 100644 --- a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/JettyServer.java +++ b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/JettyServer.java @@ -476,7 +476,7 @@ public static Server jettyServer(int minThreads, int maxThreads) { // Args reversed: Jetty uses (max,min) threadPool = new QueuedThreadPool(maxThreads, minThreads); // Server(ThreadPool) alone installs a default 64KB ArrayByteBufferPool; pass ours explicitly. - Server server = new Server(threadPool, null, newByteBufferPool()); + Server server = new Server(threadPool, (Scheduler)null, newByteBufferPool()); return server; } From 33bce2acc5f2ac5a087fc1c059d850e15e4a9422 Mon Sep 17 00:00:00 2001 From: shah Date: Wed, 29 Jul 2026 22:14:10 +0200 Subject: [PATCH 3/4] GH-4084: Address review feedback on ByteBufferPool sizing Use the pool's default factor (-1) and reduce jettyOutputBufferSize from 5MB to 2MB per review. --- .../src/main/java/org/apache/jena/fuseki/main/JettyServer.java | 2 +- .../org/apache/jena/fuseki/main/sys/FusekiSystemConstants.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/JettyServer.java b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/JettyServer.java index d2632d397dd..28c9aa0050a 100644 --- a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/JettyServer.java +++ b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/JettyServer.java @@ -484,7 +484,7 @@ public static Server jettyServer(int minThreads, int maxThreads) { * {@link FusekiSystemConstants#jettyOutputBufferSize}. */ private static ByteBufferPool newByteBufferPool() { int maxCapacity = FusekiSystemConstants.jettyOutputBufferSize; - return new ArrayByteBufferPool(0, 2048, maxCapacity, -1, -1, -1); + return new ArrayByteBufferPool(0, -1, maxCapacity, -1, -1, -1); } private static void serverAddConnectors(Server server, int port, boolean loopback) { diff --git a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/sys/FusekiSystemConstants.java b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/sys/FusekiSystemConstants.java index 5e26090a34b..2a5de07736d 100644 --- a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/sys/FusekiSystemConstants.java +++ b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/sys/FusekiSystemConstants.java @@ -55,7 +55,7 @@ private static int getValueInt(String propertyKey, int defaultValue) { * Setting for HttpConfiguration.setOutputBufferSize (set in * {@link JettyLib#httpConfiguration}). */ - public static final int jettyOutputBufferSize = 5 * 1024 * 1024; + public static final int jettyOutputBufferSize = 2 * 1024 * 1024; /** * JettyrRequest header size. From 8edb3b8b0cccb980776c12fb2755b03ff10f4d51 Mon Sep 17 00:00:00 2001 From: shah Date: Wed, 29 Jul 2026 23:17:39 +0200 Subject: [PATCH 4/4] GH-4084: Add missing Scheduler import The (Scheduler)null cast applied from the review suggestion didn't compile without this import. --- .../src/main/java/org/apache/jena/fuseki/main/JettyServer.java | 1 + 1 file changed, 1 insertion(+) diff --git a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/JettyServer.java b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/JettyServer.java index 28c9aa0050a..b08268fdb41 100644 --- a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/JettyServer.java +++ b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/JettyServer.java @@ -58,6 +58,7 @@ import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.util.thread.QueuedThreadPool; +import org.eclipse.jetty.util.thread.Scheduler; import org.eclipse.jetty.util.thread.ThreadPool; import org.eclipse.jetty.xml.XmlConfiguration; import org.slf4j.Logger;