@@ -121,6 +121,9 @@ export default class BlockDbRepository implements BlockRepository {
121121 * supporting cursor-based pagination and chain filtering for efficient
122122 * navigation through large result sets.
123123 *
124+ * Uses keyset pagination with compound cursors (height:id) for better performance
125+ * when navigating through large datasets.
126+ *
124127 * @param params - Object containing height range and pagination parameters
125128 * @returns Promise resolving to page info and block edges
126129 */
@@ -145,14 +148,140 @@ export default class BlockDbRepository implements BlockRepository {
145148 const queryParams : ( string | number | string [ ] ) [ ] = [ limit , startHeight ] ;
146149 let conditions = '' ;
147150
148- if ( before ) {
149- queryParams . push ( before ) ;
150- conditions += `\nAND b.id > $${ queryParams . length } ` ;
151+ // Handle "after" cursor (for forward pagination) - decode to get both height and id
152+ let afterHeight , afterId ;
153+ if ( after ) {
154+ try {
155+ // Try to parse as compound cursor format (height:id)
156+ const [ height , id ] = after . split ( ':' ) ;
157+ afterHeight = parseInt ( height , 10 ) ;
158+ afterId = parseInt ( id , 10 ) ;
159+
160+ // Check if values are valid numbers
161+ if ( ! isNaN ( afterHeight ) && ! isNaN ( afterId ) ) {
162+ // For forward pagination with "after", we want records where:
163+ // (height < afterHeight) OR (height = afterHeight AND id < afterId)
164+ queryParams . push ( afterHeight , afterId ) ;
165+ conditions += `\nAND (b.height < $${ queryParams . length - 1 } OR (b.height = $${ queryParams . length - 1 } AND b.id < $${ queryParams . length } ))` ;
166+ console . info (
167+ `[INFO][QUERY][OPTIMIZATION] Using optimized query with compound cursor: height=${ afterHeight } , id=${ afterId } ` ,
168+ ) ;
169+ } else {
170+ // Old cursor format - look up the height for this ID to optimize the query
171+ const idValue = parseInt ( after , 10 ) ;
172+ if ( ! isNaN ( idValue ) ) {
173+ console . info (
174+ `[INFO][QUERY][OPTIMIZATION] Converting old cursor format (id=${ idValue } ) to optimized format` ,
175+ ) ;
176+
177+ // Get the height for this ID with a fast lookup
178+ const heightQuery = `SELECT height FROM "Blocks" WHERE id = $1 LIMIT 1` ;
179+ const { rows } = await rootPgPool . query ( heightQuery , [ idValue ] ) ;
180+
181+ if ( rows . length > 0 ) {
182+ // We found the height, now use the optimized approach
183+ afterHeight = rows [ 0 ] . height ;
184+ afterId = idValue ;
185+
186+ // Use the optimized condition
187+ queryParams . push ( afterHeight , afterId ) ;
188+ conditions += `\nAND (b.height < $${ queryParams . length - 1 } OR (b.height = $${ queryParams . length - 1 } AND b.id < $${ queryParams . length } ))` ;
189+ console . info (
190+ `[INFO][QUERY][OPTIMIZATION] Converted to optimized query: height=${ afterHeight } , id=${ afterId } ` ,
191+ ) ;
192+ } else {
193+ // Fallback if we couldn't find the height
194+ queryParams . push ( after ) ;
195+ conditions += `\nAND b.id < $${ queryParams . length } ` ;
196+ console . info (
197+ `[INFO][QUERY][OPTIMIZATION] Could not find height for id=${ idValue } , using fallback approach` ,
198+ ) ;
199+ }
200+ } else {
201+ // Fallback for compatibility with old cursor format
202+ queryParams . push ( after ) ;
203+ conditions += `\nAND b.id < $${ queryParams . length } ` ;
204+ console . info (
205+ `[INFO][QUERY][OPTIMIZATION] Using fallback approach with cursor=${ after } ` ,
206+ ) ;
207+ }
208+ }
209+ } catch ( e : unknown ) {
210+ // Fallback for compatibility with old cursor format
211+ queryParams . push ( after ) ;
212+ conditions += `\nAND b.id < $${ queryParams . length } ` ;
213+ console . info (
214+ `[INFO][QUERY][OPTIMIZATION] Error parsing cursor, using fallback approach: ${ e instanceof Error ? e . message : String ( e ) } ` ,
215+ ) ;
216+ }
151217 }
152218
153- if ( after ) {
154- queryParams . push ( after ) ;
155- conditions += `\nAND b.id < $${ queryParams . length } ` ;
219+ // Handle "before" cursor (for backward pagination) - decode to get both height and id
220+ let beforeHeight , beforeId ;
221+ if ( before ) {
222+ try {
223+ // Try to parse as compound cursor format (height:id)
224+ const [ height , id ] = before . split ( ':' ) ;
225+ beforeHeight = parseInt ( height , 10 ) ;
226+ beforeId = parseInt ( id , 10 ) ;
227+
228+ // Check if values are valid numbers
229+ if ( ! isNaN ( beforeHeight ) && ! isNaN ( beforeId ) ) {
230+ // For backward pagination with "before", we want records where:
231+ // (height > beforeHeight) OR (height = beforeHeight AND id > beforeId)
232+ queryParams . push ( beforeHeight , beforeId ) ;
233+ conditions += `\nAND (b.height > $${ queryParams . length - 1 } OR (b.height = $${ queryParams . length - 1 } AND b.id > $${ queryParams . length } ))` ;
234+ console . info (
235+ `[INFO][QUERY][OPTIMIZATION] Using optimized query with compound cursor: height=${ beforeHeight } , id=${ beforeId } ` ,
236+ ) ;
237+ } else {
238+ // Old cursor format - look up the height for this ID to optimize the query
239+ const idValue = parseInt ( before , 10 ) ;
240+ if ( ! isNaN ( idValue ) ) {
241+ console . info (
242+ `[INFO][QUERY][OPTIMIZATION] Converting old cursor format (id=${ idValue } ) to optimized format` ,
243+ ) ;
244+
245+ // Get the height for this ID with a fast lookup
246+ const heightQuery = `SELECT height FROM "Blocks" WHERE id = $1 LIMIT 1` ;
247+ const { rows } = await rootPgPool . query ( heightQuery , [ idValue ] ) ;
248+
249+ if ( rows . length > 0 ) {
250+ // We found the height, now use the optimized approach
251+ beforeHeight = rows [ 0 ] . height ;
252+ beforeId = idValue ;
253+
254+ // Use the optimized condition
255+ queryParams . push ( beforeHeight , beforeId ) ;
256+ conditions += `\nAND (b.height > $${ queryParams . length - 1 } OR (b.height = $${ queryParams . length - 1 } AND b.id > $${ queryParams . length } ))` ;
257+ console . info (
258+ `[INFO][QUERY][OPTIMIZATION] Converted to optimized query: height=${ beforeHeight } , id=${ beforeId } ` ,
259+ ) ;
260+ } else {
261+ // Fallback if we couldn't find the height
262+ queryParams . push ( before ) ;
263+ conditions += `\nAND b.id > $${ queryParams . length } ` ;
264+ console . info (
265+ `[INFO][QUERY][OPTIMIZATION] Could not find height for id=${ idValue } , using fallback approach` ,
266+ ) ;
267+ }
268+ } else {
269+ // Fallback for compatibility with old cursor format
270+ queryParams . push ( before ) ;
271+ conditions += `\nAND b.id > $${ queryParams . length } ` ;
272+ console . info (
273+ `[INFO][QUERY][OPTIMIZATION] Using fallback approach with cursor=${ before } ` ,
274+ ) ;
275+ }
276+ }
277+ } catch ( e : unknown ) {
278+ // Fallback for compatibility with old cursor format
279+ queryParams . push ( before ) ;
280+ conditions += `\nAND b.id > $${ queryParams . length } ` ;
281+ console . info (
282+ `[INFO][QUERY][OPTIMIZATION] Error parsing cursor, using fallback approach: ${ e instanceof Error ? e . message : String ( e ) } ` ,
283+ ) ;
284+ }
156285 }
157286
158287 if ( chainIds ?. length ) {
@@ -165,6 +294,9 @@ export default class BlockDbRepository implements BlockRepository {
165294 conditions += `\nAND b."height" <= $${ queryParams . length } ` ;
166295 }
167296
297+ // Record the start time to measure performance
298+ const startTime = Date . now ( ) ;
299+
168300 const query = `
169301 SELECT b.id,
170302 b.hash,
@@ -182,14 +314,21 @@ export default class BlockDbRepository implements BlockRepository {
182314 FROM "Blocks" b
183315 WHERE b.height >= $2
184316 ${ conditions }
185- ORDER BY b.height ${ order }
317+ ORDER BY b.height ${ order } , b.id ${ order }
186318 LIMIT $1;
187319 ` ;
188320
189321 const { rows : blockRows } = await rootPgPool . query ( query , queryParams ) ;
190322
323+ // Log the query performance
324+ const endTime = Date . now ( ) ;
325+ console . info (
326+ `[INFO][QUERY][PERFORMANCE] Block query executed in ${ endTime - startTime } ms, returned ${ blockRows . length } rows` ,
327+ ) ;
328+
329+ // Create edges with a compound cursor containing both height and id
191330 const edges = blockRows . map ( row => ( {
192- cursor : row . id . toString ( ) ,
331+ cursor : ` ${ row . height } : ${ row . id } ` , // Compound cursor for better pagination
193332 node : blockValidator . validate ( row ) ,
194333 } ) ) ;
195334
0 commit comments