CORDA-3176 Inefficient query generated on vault queries with custom p… (#6241)

* 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
This commit is contained in:
Tamas Veingartner 2020-06-02 10:33:59 +01:00 committed by GitHub
parent 14b9bc2c53
commit 9c4a76d367
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 42 additions and 5 deletions

View File

@ -612,12 +612,15 @@ class HibernateQueryCriteriaParser(val contractStateType: Class<out ContractStat
entityRoot
}
val joinPredicate = if(IndirectStatePersistable::class.java.isAssignableFrom(entityRoot.javaType)) {
criteriaBuilder.equal(vaultStates.get<PersistentStateRef>("stateRef"), entityRoot.get<IndirectStatePersistable<*>>("compositeKey").get<PersistentStateRef>("stateRef"))
} else {
criteriaBuilder.equal(vaultStates.get<PersistentStateRef>("stateRef"), entityRoot.get<PersistentStateRef>("stateRef"))
if (entityRoot != vaultStates){ // to avoid self join
val joinPredicate = if(IndirectStatePersistable::class.java.isAssignableFrom(entityRoot.javaType)) {
criteriaBuilder.equal(vaultStates.get<PersistentStateRef>("stateRef"), entityRoot.get<IndirectStatePersistable<*>>("compositeKey").get<PersistentStateRef>("stateRef"))
}
else {
criteriaBuilder.equal(vaultStates.get<PersistentStateRef>("stateRef"), entityRoot.get<PersistentStateRef>("stateRef"))
}
predicateSet.add(joinPredicate)
}
predicateSet.add(joinPredicate)
// resolve general criteria expressions
@Suppress("UNCHECKED_CAST")

View File

@ -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<StateAndRef<*>>()
var pageNumber = 0
while (pageNumber * pageSize < numberOfStates) {
val paging = PageSpecification(pageNumber = pageNumber + 1, pageSize = pageSize)
val page = vaultService.queryBy<DummyLinearContract.State>(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`() {