SKIP/LIMIT offset exhaustion in Neo4j large-dataset pagination
0 reputation · 22 Jul 2020, 15:57 UTC
0 reputation · 22 Jul 2020, 15:57 UTC
A Neo4j query must paginate through a vertex collection exceeding ten million records while preserving response latency below two seconds in current releases. The Cypher SKIP and LIMIT clauses constitute the documented offset-based pagination mechanism, yet their performance characteristics degrade as the offset increases because the engine materializes and discards every preceding node before yielding the requested page. This behavior necessitates diagnosing the offset threshold at which index usage becomes ineffective and full scan dominates execution cost.
Concurrent graph modifications introduce uncertainty into pagination stability: without a globally unique, immutable sorting property, cursor-based keyset pagination may skip or duplicate records across sequential fetches. Additionally, mixed node labels and varying degree distributions further obscure the offset point where SKIP/LIMIT transitions from index-assisted traversal to exhaustive scan. Under what offset value does SKIP/LIMIT shift from index-assisted to full-scan traversal? Can keyset pagination guarantee completeness when the ordering property is non-unique and subject to concurrent mutation? How does label heterogeneity affect the reliability of cursor continuation across paginated calls?
There is no single, universal offset value where Neo4j shifts from an index-assisted seek to a full scan. Instead, SKIP operates with linear O(n) complexity. The engine must materialize and discard every record preceding the offset before returning the requested page. In a dataset of ten million records, you will likely violate a two-second latency requirement long before reaching the end of the set, as the cumulative cost of db hits increases incrementally with each page.
Keyset pagination (cursor-based) maintains O(log n) performance by using a WHERE clause to jump directly to the next starting point via an index. However, it cannot guarantee completeness or uniqueness if the ordering property is non-unique or subject to concurrent mutation:
Label heterogeneity affects the initial entry point of the query. A query targeting a specific label using a label-specific index is significantly more efficient than a generic node scan. However, once the traversal begins, the reliability of the cursor depends entirely on the index used for the sort property, not the labels themselves. If the cursor relies on a property that is not consistently present across all labels in the result set, those nodes may be excluded from the pagination sequence.
To maintain sub-two-second latency across ten million records, replace SKIP/LIMIT with a keyset approach using a unique tie-breaker (such as a UUID or the internal element ID) to ensure stability.
// Example: Keyset pagination with a unique tie-breaker
MATCH (n:User)
WHERE (n.timestamp < $lastTimestamp)
OR (n.timestamp = $lastTimestamp AND n.uuid < $lastUuid)
RETURN n
ORDER BY n.timestamp DESC, n.uuid DESC
LIMIT 25
PROFILE on your current SKIP query. Observe the db hits metric as you increase the offset; you will see a linear increase in cost.PROFILE on the keyset query. The db hits should remain nearly constant regardless of how deep you are in the dataset.Diagnostic Requirement: Please specify if your sorting property is immutable. If the property used for the cursor is updated frequently, a different architectural approach (such as a materialized snapshot) may be required to prevent result drift.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.