Skip to content

Commit

Permalink
Fix IndexOutOfBounds after getRefreshKey
Browse files Browse the repository at this point in the history
It's possible that on a paging source that's not in the initial
generation, we fail with an `IndexOutOfBoundsException`. This
is caused when the underlying data source undergoes a delete, which
then invalidates the paging source, which then calls
`getRefreshKey`. In the event that the the refresh key is the
very last index of the previous paging source, we would fail
with the above exception because that index does not exist
in the next generation paging source.

Closes cashapp#2434.
  • Loading branch information
kevincianfarini committed Jul 15, 2021
1 parent b50f0b8 commit 4bd7497
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 4 deletions.
Expand Up @@ -34,9 +34,13 @@ internal class OffsetQueryPagingSource<RowType : Any>(
params: LoadParams<Long>
): LoadResult<Long, RowType> = withContext(dispatcher) {
try {
val key = params.key ?: 0L
transacter.transactionWithResult {
val count = countQuery.executeAsOne()
val key = when (params) {
is LoadParams.Refresh -> params.key?.let { notNullKey -> minOf(count - 1, notNullKey) }
else -> params.key
} ?: 0L

if (count != 0L && key >= count) throw IndexOutOfBoundsException()

val loadSize = if (key < 0) params.loadSize + key else params.loadSize
Expand Down
Expand Up @@ -47,8 +47,7 @@ class OffsetQueryPagingSourceTest {
transacter = object : TransacterImpl(driver) {}
}

@After
fun after() {
@After fun after() {
File("test.db").delete()
}

Expand Down Expand Up @@ -241,11 +240,25 @@ class OffsetQueryPagingSourceTest {

runBlocking {
assertFailsWith<IndexOutOfBoundsException> {
source.load(Refresh(10, 2, false))
source.load(PagingSource.LoadParams.Append(10, 2, false))
}
}
}

@Test fun `refresh key too big gets truncated`() {
val source = OffsetQueryPagingSource(
this::query,
countQuery(),
transacter,
TestCoroutineDispatcher()
)

runBlocking {
val results = source.load(Refresh(10, 2, false))
assertEquals(listOf(9L), (results as LoadResult.Page).data)
}
}

@Test fun `query invaliation invalidates paging source`() {
val query = query(2, 0)
val source = OffsetQueryPagingSource(
Expand Down

0 comments on commit 4bd7497

Please sign in to comment.