Page research without exact counts and bound candidate scans [skip ci]

This commit is contained in:
2026-11-07 17:27:35 +08:00
parent 142c7b2d86
commit 026ee22c1e
8 changed files with 563 additions and 86 deletions
+60 -9
View File
@@ -46,7 +46,8 @@ not require a session; these routes do not expose private data or accept writes.
List parameters are `q` (200 Unicode characters, 20 words), `searchIn`
(`text` or `author`), `topic` (an exact arXiv category), `sort` (`recent` or
`title`), `page` (1–1000), and `pageSize` (1–50, default 8). Unknown, malformed
`title`), `cursor` (optional, opaque, at most 32768 characters), and
`pageSize` (1–50, default 8). Unknown, malformed
or repeated parameters return 400. `recent` means descending stable paper ID,
not publication date. Search terms must become bound query parameters, never
raw SQL or user-supplied full-text syntax.
@@ -61,17 +62,35 @@ those two missing access paths. It is separate from Core startup and auth
migrations; do not grant DDL to the runtime reader. Both indexes completed on the confirmed ECS (category 35.4 seconds, author
436.47 seconds); measured end-to-end checks are documented below.
List responses contain `items`, `total`, `topics`, `page`, and `pageSize`.
List responses contain `items`, `nextCursor`, `previousCursor`, `topics`, and
`pageSize`. Empty cursor strings mean that direction is unavailable. There is
no `total`, page number, total page count or deep-page cap. The old `page`
parameter is rejected; coordinate the frontend/backend contract update.
Each paper preserves original title/abstract, authors, source dates, topics and
source links. Publication decisions need source evidence; absent decisions stay
unknown. No invented repositories, metrics or generated research conclusions.
The repository counts and reads each list in one read-only snapshot.
Paging first materializes matching IDs, then joins only that page to the wide
paper rows. Filtered searches first materialize matching IDs without an
ORDER BY/LIMIT, then sort/page those IDs, preventing sparse terms from choosing
a backward heap scan with repeated text-vector computation. Activity sampling showed that selecting full rows before LIMIT
caused sparse category queries to read large portions of the paper heap. The
Lists use a read-only repeatable-read snapshot without `count(*)` or `OFFSET`.
Keyset paging uses ID descending for recent order and `(title_key, id)` ascending
for title order, including a reverse query for previous pages. The title key is
kept in its stored codec and C collation. Cursors bind normalized query, search
scope, topic, sort and page size; changing filters requires a fresh cursor. They
are public row positions, not authorization tokens or guaranteed frozen snapshots
across requests. Concurrent corpus edits may alter later pages.
Only pageSize+1 positions are selected; the extra position indicates more data,
then full rows and batched metadata are loaded for at most pageSize papers.
For recent order with only a topic filter, page directly through the existing
`(tag_id,paper_id)` index and deduplicate evidence before LIMIT. For text/author
search in recent order, try an ID interval of 512 near the cursor, expanding
once to 4096 only if there are some matches but fewer than pageSize+1.
Only a full page plus one proves that interval contains the next page. If neither bounded attempt fills the page,
all local candidates are discarded and the complete indexed search is used;
rare or older matches are never silently truncated. The fallback materializes
matching IDs before sorting, avoiding an unbounded ordered heap scan for sparse
terms. Title-order filtered queries still use that complete search and can time
out for broad terms. No global database or proxy resource settings are changed.
The
HTTP layer permits two in-flight queries with an eight-second context budget.
Busy requests return 429 with `Retry-After: 1`; deadlines return 504; storage
errors return a generic 503 without database details. Missing papers return 404.
@@ -95,7 +114,7 @@ and homepage HTTP routes have returned 200.
The frontend has also rendered the actual 3,526,999-paper corpus through a
loopback SSH tunnel: eight rows, matching detail title/source, desktop and
mobile layout, no page exceptions or horizontal overflow in that check.
Author search returned 146 records in 0.336 seconds. After paging/author fixes, category `cs.AR` returned 9,457 records in 4.906
The earlier count-based API returned 146 author records in 0.336 seconds. Before the cursor optimization, after paging/author fixes, category `cs.AR` returned 9,457 records in 4.906
seconds, `CartoonGAN` returned three in 0.332 seconds, and the multi-term search
`attention is all you need` returned 8,653 in 6.325 seconds. These were sequential
manual HTTP checks, not a load test. Very broad text searches can still exceed
@@ -143,3 +162,35 @@ reload/new-tab persistence and logout behavior. Browser control was unavailable,
so these browser results are attributed to user acceptance rather than an
automated real-account test. This does not deploy the research work branch;
mail delivery was not exercised.
## Cursor regression coverage
The disposable PostgreSQL fixture covers forward/backward traversal, identical
title keys with different IDs, published control-character encoding, sparse
matches separated by a large ID gap, and fallback from the bounded interval.
Invalid, oversized, cross-filter and wrong-version cursors are rejected before
repository execution. The runtime reader retains SELECT-only privileges; no DDL
or writes are added. Run tests manually on the confirmed ECS; skip CI for this
iteration and retain `[skip ci]` in commit and PR titles.
## Manual cursor measurements (2026-11-07)
On the confirmed ECS, after this task's compilation and disposable fixture had
finished, sequential direct requests to the temporary Core returned: homepage
65 ms, next/previous 39/21 ms, `graph` 345 ms, `CartoonGAN` 731 ms (3 papers),
`cs.AR` 145 ms, title order 180 ms, author `Buhrman` 769 ms, and
`attention is all you need` 1312 ms. Recent, topic and title cursor round trips
returned the same first page without overlap. These are individual manual
samples, not production latency guarantees; concurrent compilation earlier
produced multi-second cold measurements. No proxy, database memory, CPU or
production configuration was changed.
The final source compiled manually on the ECS; package unit checks and all ten
selected cursor/PostgreSQL integration tests passed, including interval expansion
and complete fallback. No CI or local build ran. The local authorized Next dev
uses the temporary API through the existing SSH tunnel. Independent Chrome also
verified real homepage/search navigation, a preserved document, one mobile
header/main, no page exceptions and no horizontal overflow. The production Core
was not replaced by these temporary preview updates.