From 9c4a76d367c70061d29c3bb9c1ace7e721f0740d Mon Sep 17 00:00:00 2001 From: Tamas Veingartner Date: Tue, 2 Jun 2020 10:33:59 +0100 Subject: [PATCH] =?UTF-8?q?CORDA-3176=20Inefficient=20query=20generated=20?= =?UTF-8?q?on=20vault=20queries=20with=20custom=20p=E2=80=A6=20(#6241)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * CORDA-3176 Inefficient query generated on vault queries with custom paging a check added to avoid self joins local postgres db tests were executed on large volume test data (50k states) to ensure that the DB optimizes the suspicious query and so it does not cuase performance issues. A test added to check that a pagination query on large volume of sorted data is completed in a reasonable time * foreach detekt fix --- .../vault/HibernateQueryCriteriaParser.kt | 13 ++++--- .../node/services/vault/VaultQueryTests.kt | 34 +++++++++++++++++++ 2 files changed, 42 insertions(+), 5 deletions(-) diff --git a/node/src/main/kotlin/net/corda/node/services/vault/HibernateQueryCriteriaParser.kt b/node/src/main/kotlin/net/corda/node/services/vault/HibernateQueryCriteriaParser.kt index c26c414019..c6ee750546 100644 --- a/node/src/main/kotlin/net/corda/node/services/vault/HibernateQueryCriteriaParser.kt +++ b/node/src/main/kotlin/net/corda/node/services/vault/HibernateQueryCriteriaParser.kt @@ -612,12 +612,15 @@ class HibernateQueryCriteriaParser(val contractStateType: Class("stateRef"), entityRoot.get>("compositeKey").get("stateRef")) - } else { - criteriaBuilder.equal(vaultStates.get("stateRef"), entityRoot.get("stateRef")) + if (entityRoot != vaultStates){ // to avoid self join + val joinPredicate = if(IndirectStatePersistable::class.java.isAssignableFrom(entityRoot.javaType)) { + criteriaBuilder.equal(vaultStates.get("stateRef"), entityRoot.get>("compositeKey").get("stateRef")) + } + else { + criteriaBuilder.equal(vaultStates.get("stateRef"), entityRoot.get("stateRef")) + } + predicateSet.add(joinPredicate) } - predicateSet.add(joinPredicate) // resolve general criteria expressions @Suppress("UNCHECKED_CAST") diff --git a/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt b/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt index 2a5f12e994..f66e903bc4 100644 --- a/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt @@ -501,6 +501,40 @@ abstract class VaultQueryTestsBase : VaultQueryParties { assertThat(queriedStates).containsExactlyElementsOf(allStates) } } + + @Test(timeout=300_000) + fun `query with sort criteria and pagination on large volume of states should complete in time`() { + val numberOfStates = 1000 + val pageSize = 1000 + + for (i in 1..50) { + database.transaction { + vaultFiller.fillWithSomeTestLinearStates(numberOfStates, linearNumber = 100L) + } + } + + val criteria = VaultQueryCriteria(status = Vault.StateStatus.ALL) + + val sortAttribute = SortAttribute.Custom(DummyLinearStateSchemaV1.PersistentDummyLinearState::class.java, "stateRef") + + Sort.Direction.values().forEach { sortDirection -> + + val sorting = Sort(listOf(Sort.SortColumn(sortAttribute, sortDirection))) + + val start = System.currentTimeMillis() + val queriedStates = mutableListOf>() + var pageNumber = 0 + while (pageNumber * pageSize < numberOfStates) { + val paging = PageSpecification(pageNumber = pageNumber + 1, pageSize = pageSize) + val page = vaultService.queryBy(sorting = sorting, paging = paging, criteria = criteria) + queriedStates += page.states + pageNumber++ + } + + val elapsed = System.currentTimeMillis() - start + assertThat(elapsed).isLessThan(1000) + } + } @Test(timeout=300_000) fun `unconsumed states with count`() {