From c131b91ceb95a1f16be7bd022bd3bba9f808f39d Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Wed, 16 Sep 2026 08:39:16 -0700 Subject: [PATCH] Raise recent-feed total scan cap to 500 GET /posts/recent now caps its total scan at 500 eligible top-level posts instead of 10, so corpora smaller than the cap report an exact total while larger corpora stay bounded. Added unit coverage for the exact-total-under-cap and default-cap cases, plus the many-top-level-posts acceptance fixture. By coder. --- psf-memo-db/acceptance/lib/handlers.js | 30 +++++++++++++++++++ psf-memo-db/src/adapters/post-query.js | 2 +- .../test/unit/adapters/post-query.unit.js | 21 +++++++++++-- 3 files changed, 50 insertions(+), 3 deletions(-) diff --git a/psf-memo-db/acceptance/lib/handlers.js b/psf-memo-db/acceptance/lib/handlers.js index 0030719..9ae0880 100644 --- a/psf-memo-db/acceptance/lib/handlers.js +++ b/psf-memo-db/acceptance/lib/handlers.js @@ -209,6 +209,11 @@ async function loadFixture (world, name) { return } + if (name === 'many-top-level-posts') { + await loadManyTopLevelPosts(world) + return + } + if (name !== 'three-top-level-posts-and-one-reply') { throw new Error(`Unknown fixture: ${name}`) } @@ -376,6 +381,31 @@ async function loadManyPostsWithReplies (world) { } } +async function loadManyTopLevelPosts (world) { + // 510 eligible top-level posts: larger than the 500 total-scan cap, so the + // capped total is exactly 500, while an offset near the end exhausts the + // index and reads all 510 entries. + for (let i = 0; i < 510; i++) { + const id = String(i).padStart(3, '0') + const txid = `post-${id}` + const blockHeight = 600000 + i + await world.adapters.level.postsDb.put(txid, { + addr: 'bitcoincash:qaddr', + text: `post ${id}`, + seen: i, + blockHeight + }) + await world.adapters.level.postHeightsDb.put( + String(blockHeight).padStart(12, '0') + ':' + txid, + { txid, blockHeight } + ) + await world.adapters.level.addrPostHeightsDb.put( + `bitcoincash:qaddr:${String(blockHeight).padStart(12, '0')}:${txid}`, + { txid, addr: 'bitcoincash:qaddr', blockHeight } + ) + } +} + async function backfillIndexes (world) { const posts = [] for await (const [txid, post] of world.adapters.level.postsDb.iterator()) { diff --git a/psf-memo-db/src/adapters/post-query.js b/psf-memo-db/src/adapters/post-query.js index 0f112da..13d3dee 100644 --- a/psf-memo-db/src/adapters/post-query.js +++ b/psf-memo-db/src/adapters/post-query.js @@ -11,7 +11,7 @@ import { getPostOrNull as getPostOrNullShared } from './lib/get-post-or-null.js' import { loadMutedAddrs, isMutedPost } from './lib/muted-posts.js' const HEIGHT_PAD = 12 -const TOTAL_SCAN_CAP = 10 +const TOTAL_SCAN_CAP = 500 class PostQuery { constructor (localConfig = {}) { diff --git a/psf-memo-db/test/unit/adapters/post-query.unit.js b/psf-memo-db/test/unit/adapters/post-query.unit.js index a70143d..780a6bc 100644 --- a/psf-memo-db/test/unit/adapters/post-query.unit.js +++ b/psf-memo-db/test/unit/adapters/post-query.unit.js @@ -162,7 +162,7 @@ describe('#PostQuery', () => { }) describe('#scanRecentPostTxidsAndCount', () => { - it('should return txids and a capped total count', async () => { + it('should report the exact total when the index is smaller than the default cap', async () => { async function * mockHeights () { for (let i = 20; i >= 0; i--) { const id = String(i).padStart(3, '0') @@ -174,7 +174,24 @@ describe('#PostQuery', () => { const result = await uut.scanRecentPostTxidsAndCount({ limit: 3, offset: 0 }) assert.deepEqual(result.txids, ['post-020', 'post-019', 'post-018']) - assert.equal(result.total, 10) + assert.equal(result.total, 21) + }) + + it('should default the total scan cap to 500', async () => { + let reads = 0 + async function * mockHeights () { + for (let i = 599; i >= 0; i--) { + reads++ + const id = String(i).padStart(3, '0') + yield [`000000${600000 + i}:post-${id}`, { txid: `post-${id}` }] + } + } + postHeightsDb.iterator.withArgs({ reverse: true }).returns(mockHeights()) + + const result = await uut.scanRecentPostTxidsAndCount({ limit: 3, offset: 0 }) + + assert.equal(result.total, 500) + assert.equal(reads, 503) // offset + limit + default cap }) it('should cap the raw postHeights scan to limit + offset + cap', async () => {