This is an automated email from the ASF dual-hosted git repository. reschke pushed a commit to branch SLING-13284-x in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-resourceresolver.git
commit 351b6a7b8f50d9461ecd63b57f9a29dac7e5a90d Author: Sagar Miglani <[email protected]> AuthorDate: Tue Aug 4 15:22:47 2026 +0530 SLING-13284 - Do not abort alias/vanity cache init on out-of-order qu… (#225) * SLING-13284 - Do not abort alias/vanity cache init on out-of-order query results Demote the ordering checks in PagedQueryIterator from fatal throws to log.warn. The checks compared live property values against an async index sort order, which can lag after a property write. * SLING-13284 - Remove unused QueryImplementationException --- .../impl/mapping/PagedQueryIterator.java | 33 ++++++------ .../impl/mapping/PagedQueryIteratorTest.java | 58 ++++++++++++++++++---- 2 files changed, 63 insertions(+), 28 deletions(-) diff --git a/src/main/java/org/apache/sling/resourceresolver/impl/mapping/PagedQueryIterator.java b/src/main/java/org/apache/sling/resourceresolver/impl/mapping/PagedQueryIterator.java index 40356f06..2081daa9 100644 --- a/src/main/java/org/apache/sling/resourceresolver/impl/mapping/PagedQueryIterator.java +++ b/src/main/java/org/apache/sling/resourceresolver/impl/mapping/PagedQueryIterator.java @@ -90,18 +90,22 @@ public class PagedQueryIterator implements Iterator<Resource> { if (values.length > 0) { String value = values[0]; if (value.compareTo(lastKey) < 0) { - String message = String.format( - "unexpected query result in page %d, property name '%s', got '%s', despite querying for > '%s'", - (page - 1), propertyName, value, lastKey); - log.error(message); - throw new QueryImplementationException(message); + log.warn( + "unexpected query result in page {}, property name '{}', got '{}', despite querying for > '{}'" + + " (the async index may not yet reflect the current property value)", + (page - 1), + propertyName, + value, + lastKey); } if (lastValue != null && value.compareTo(lastValue) < 0) { - String message = String.format( - "unexpected query result in page %d, property name '%s', got '%s', last value was '%s'", - (page - 1), propertyName, value, lastValue); - log.error(message); - throw new QueryImplementationException(message); + log.warn( + "unexpected query result in page {}, property name '{}', got '{}', last value was '{}'" + + " (the async index may not yet reflect the current property value)", + (page - 1), + propertyName, + value, + lastValue); } // keep information about large key counts @@ -178,13 +182,4 @@ public class PagedQueryIterator implements Iterator<Resource> { return ""; } } - - /** - * Thrown when the underlying repository misbehaves with respect to sorting on multivalued properties. - */ - public static class QueryImplementationException extends RuntimeException { - public QueryImplementationException(String message) { - super(message); - } - } } diff --git a/src/test/java/org/apache/sling/resourceresolver/impl/mapping/PagedQueryIteratorTest.java b/src/test/java/org/apache/sling/resourceresolver/impl/mapping/PagedQueryIteratorTest.java index 61e1cc5e..24ac0777 100644 --- a/src/test/java/org/apache/sling/resourceresolver/impl/mapping/PagedQueryIteratorTest.java +++ b/src/test/java/org/apache/sling/resourceresolver/impl/mapping/PagedQueryIteratorTest.java @@ -86,31 +86,71 @@ public class PagedQueryIteratorTest extends AbstractMappingMapEntriesTest { assertEquals("", it.getWarning()); } - @Test(expected = PagedQueryIterator.QueryImplementationException.class) + @Test public void testSimpleWrongOrder() { + // SLING-13284: out-of-order results (from a stale async index) should not abort iteration String[] expected = new String[] {"a", "b", "d", "c"}; Collection<Resource> expectedResources = toResourceList(expected); when(resourceResolver.findResources(eq("testSimpleWrongOrder"), eq("JCR-SQL2"))) .thenReturn(expectedResources.iterator()); - // incorrect sort order within a query page - Iterator<Resource> it = + PagedQueryIterator it = new PagedQueryIterator("alias", PROPNAME, resourceResolver, "testSimpleWrongOrder", 2000); - while (it.hasNext()) { - it.next(); - } + checkResult(it, expected); } - @Test(expected = PagedQueryIterator.QueryImplementationException.class) + @Test public void testSimpleWrongResultAfterKey() { + // SLING-13284: out-of-order results across page boundaries should not abort iteration String[] expected = new String[] {"x", "x", "a", "a"}; Collection<Resource> expectedResources = toResourceList(expected); when(resourceResolver.findResources("testSimpleWrongOrder", "JCR-SQL2")) .thenReturn(expectedResources.iterator()); - // incorrect return value based on previous key - Iterator<Resource> it = new PagedQueryIterator("alias", PROPNAME, resourceResolver, "testSimpleWrongOrder", 1); + PagedQueryIterator it = new PagedQueryIterator("alias", PROPNAME, resourceResolver, "testSimpleWrongOrder", 1); + int count = 0; + while (it.hasNext()) { + it.next(); + count++; + } + assertEquals(3, count); + } + + @Test + public void testWrongResultBelowPageBoundary() { + // SLING-13284: a result on a new page has a value below the page boundary key. + // Page 1: ["b", "c"] with pageSize=1 — "c" triggers page break (lastKey="c"). + // Page 2: returns "a" which is < lastKey "c" — must not abort. + Collection<Resource> page1 = toResourceList("b", "c"); + Collection<Resource> page2 = toResourceList("a"); + when(resourceResolver.findResources("boundary ''", "JCR-SQL2")).thenReturn(page1.iterator()); + when(resourceResolver.findResources("boundary 'c'", "JCR-SQL2")).thenReturn(page2.iterator()); + + PagedQueryIterator it = new PagedQueryIterator("alias", PROPNAME, resourceResolver, "boundary '%s'", 1); + int count = 0; + while (it.hasNext()) { + it.next(); + count++; + } + assertEquals(2, count); + } + + @Test + public void testStaleAsyncIndexDoesNotAbortIteration() { + // SLING-13284: Reproduces the scenario where an async index delivers rows in an order + // that no longer matches the live property values (e.g. sling:alias was rewritten + // between the last index cycle and a restart). All rows must still be processed. + // The descending order simulates what happens when the index sorts by a stale + // first([sling:alias]) value that no longer matches the current values[0]. + String[] staleOrder = new String[] {"ayacucho-fleeces", "ayacucho-bamboo", "ayacucho"}; + Collection<Resource> resources = toResourceList(staleOrder); + when(resourceResolver.findResources("staleIndex", "JCR-SQL2")).thenReturn(resources.iterator()); + + PagedQueryIterator it = new PagedQueryIterator("alias", PROPNAME, resourceResolver, "staleIndex", 2000); + int count = 0; while (it.hasNext()) { it.next(); + count++; } + assertEquals("all rows must be processed even when order doesn't match", 3, count); } @Test
