From cc3257a5ea53221362704afde1d9d87ae7210acd Mon Sep 17 00:00:00 2001 From: Craig Raw Date: Mon, 14 Sep 2026 13:59:10 +0200 Subject: [PATCH] floor the electrum batch page size at one when halving it after a timeout --- .../sparrow/net/PagedBatchRequestBuilder.java | 4 +- .../net/PagedBatchRequestBuilderTest.java | 37 ++++++++++++++++++- 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/sparrowwallet/sparrow/net/PagedBatchRequestBuilder.java b/src/main/java/com/sparrowwallet/sparrow/net/PagedBatchRequestBuilder.java index 56a8bbd9..306c442d 100644 --- a/src/main/java/com/sparrowwallet/sparrow/net/PagedBatchRequestBuilder.java +++ b/src/main/java/com/sparrowwallet/sparrow/net/PagedBatchRequestBuilder.java @@ -229,11 +229,11 @@ public class PagedBatchRequestBuilder extends AbstractBuilder { pageSize = DEFAULT_PAGE_SIZE; } - //Halve the page size if there have been timeouts + //Halve the page size if there have been timeouts, but never below one request, which is as small as a page can be if(transport instanceof TimeoutCounter timeoutCounter) { int timeouts = timeoutCounter.getTimeoutCount(); if(timeouts > 0) { - return pageSize / 2; + return Math.max(1, pageSize / 2); } } diff --git a/src/test/java/com/sparrowwallet/sparrow/net/PagedBatchRequestBuilderTest.java b/src/test/java/com/sparrowwallet/sparrow/net/PagedBatchRequestBuilderTest.java index df624ff1..4c2a6fcd 100644 --- a/src/test/java/com/sparrowwallet/sparrow/net/PagedBatchRequestBuilderTest.java +++ b/src/test/java/com/sparrowwallet/sparrow/net/PagedBatchRequestBuilderTest.java @@ -130,10 +130,29 @@ public class PagedBatchRequestBuilderTest { assertEquals(2, transport.requests.size(), "pageSize set before keysType and returnType must be carried onto the copies they return"); } - @SuppressWarnings("unchecked") + /** + * A timeout halves the page size, and a page size of one halved to zero, which Lists.partition rejects - failing every paged request for the rest of + * the session on a configured maximum of one. One request is as small as a page can be, and a larger size is still halved. + */ + @Test + public void pageSizeHalvedAfterATimeoutIsNeverBelowOne() throws Exception { + FakeBatchTransport transport = new TimingOutBatchTransport(); + builder(transport, 1).executeTolerant(1, "ERROR", e -> {}); + assertEquals(6, transport.requests.size()); + + FakeBatchTransport halved = new TimingOutBatchTransport(); + builder(halved, 4).executeTolerant(1, "ERROR", e -> {}); + assertEquals(3, halved.requests.size()); + } + private PagedBatchRequestBuilder builder(Transport transport) { + return builder(transport, 2); + } + + @SuppressWarnings("unchecked") + private PagedBatchRequestBuilder builder(Transport transport, int pageSize) { PagedBatchRequestBuilder batchRequest = - (PagedBatchRequestBuilder)PagedBatchRequestBuilder.create(transport, new AtomicLong()).keysType(String.class).returnType(String.class).pageSize(2); + (PagedBatchRequestBuilder)PagedBatchRequestBuilder.create(transport, new AtomicLong()).keysType(String.class).returnType(String.class).pageSize(pageSize); for(String id : List.of("a", "b", "c", "d", "e", "f")) { batchRequest.add(id, "test.method", id); } @@ -182,4 +201,18 @@ public class PagedBatchRequestBuilderTest { return MAPPER.writeValueAsString(responses); } } + + /** + * A transport that has seen a timeout, which is what the page size is halved on. + */ + private static class TimingOutBatchTransport extends FakeBatchTransport implements TimeoutCounter { + TimingOutBatchTransport() { + super(Set.of(), 0); + } + + @Override + public int getTimeoutCount() { + return 1; + } + } }