Compare commits

...
Author SHA1 Message Date
Peter Steinberger 94380d9724 fix: avoid paginated fallback in skills count query (#76) 2026-02-17 00:30:05 +01:00
4 changed files with 16 additions and 36 deletions
+2
View File
@@ -48,6 +48,7 @@ import type * as lib_githubImport from "../lib/githubImport.js";
import type * as lib_githubProfileSync from "../lib/githubProfileSync.js";
import type * as lib_githubRestoreHelpers from "../lib/githubRestoreHelpers.js";
import type * as lib_githubSoulBackup from "../lib/githubSoulBackup.js";
import type * as lib_globalStats from "../lib/globalStats.js";
import type * as lib_httpHeaders from "../lib/httpHeaders.js";
import type * as lib_httpRateLimit from "../lib/httpRateLimit.js";
import type * as lib_leaderboards from "../lib/leaderboards.js";
@@ -137,6 +138,7 @@ declare const fullApi: ApiFromModules<{
"lib/githubProfileSync": typeof lib_githubProfileSync;
"lib/githubRestoreHelpers": typeof lib_githubRestoreHelpers;
"lib/githubSoulBackup": typeof lib_githubSoulBackup;
"lib/globalStats": typeof lib_globalStats;
"lib/httpHeaders": typeof lib_httpHeaders;
"lib/httpRateLimit": typeof lib_httpRateLimit;
"lib/leaderboards": typeof lib_leaderboards;
+12 -24
View File
@@ -2,14 +2,14 @@ import type { Doc } from '../_generated/dataModel'
import type { MutationCtx, QueryCtx } from '../_generated/server'
export const GLOBAL_STATS_KEY = 'default'
const GLOBAL_STATS_PAGE_SIZE = 500
type SkillVisibilityFields = Pick<
Doc<'skills'>,
'softDeletedAt' | 'moderationStatus' | 'moderationFlags'
>
type DbCtx = Pick<MutationCtx | QueryCtx, 'db'>
type GlobalStatsReadCtx = Pick<MutationCtx | QueryCtx, 'db'>
type GlobalStatsWriteCtx = Pick<MutationCtx, 'db'>
export function isPublicSkillDoc(skill: SkillVisibilityFields | null | undefined) {
if (!skill || skill.softDeletedAt) return false
@@ -52,32 +52,20 @@ export function isGlobalStatsStorageNotReadyError(error: unknown) {
)
}
export async function countPublicSkillsForGlobalStats(ctx: DbCtx) {
export async function countPublicSkillsForGlobalStats(ctx: GlobalStatsReadCtx) {
const skills = await ctx.db
.query('skills')
.withIndex('by_active_updated', (q) => q.eq('softDeletedAt', undefined))
.collect()
let count = 0
let cursor: string | null = null
while (true) {
const { page, isDone, continueCursor } = await ctx.db
.query('skills')
.withIndex('by_active_updated', (q) => q.eq('softDeletedAt', undefined))
.order('asc')
.paginate({ cursor, numItems: GLOBAL_STATS_PAGE_SIZE })
for (const skill of page) {
if (isPublicSkillDoc(skill)) {
count += 1
}
}
if (isDone) break
cursor = continueCursor
for (const skill of skills) {
if (isPublicSkillDoc(skill)) count += 1
}
return count
}
export async function setGlobalPublicSkillsCount(
ctx: DbCtx,
ctx: GlobalStatsWriteCtx,
count: number,
now = Date.now(),
) {
@@ -104,7 +92,7 @@ export async function setGlobalPublicSkillsCount(
}
export async function adjustGlobalPublicSkillsCount(
ctx: DbCtx,
ctx: GlobalStatsWriteCtx,
delta: number,
now = Date.now(),
) {
@@ -139,7 +127,7 @@ export async function adjustGlobalPublicSkillsCount(
await ctx.db.patch(existing._id, { activeSkillsCount: nextCount, updatedAt: now })
}
export async function readGlobalPublicSkillsCount(ctx: DbCtx) {
export async function readGlobalPublicSkillsCount(ctx: GlobalStatsReadCtx) {
try {
const stats = await ctx.db
.query('globalStats')
+1
View File
@@ -53,6 +53,7 @@ export function toPublicUser(user: Doc<'users'> | null | undefined): PublicUser
}
export function toPublicSkill(skill: Doc<'skills'> | null | undefined): PublicSkill | null {
if (!skill) return null
if (!isPublicSkillDoc(skill)) return null
const stats = {
downloads:
+1 -12
View File
@@ -14,18 +14,7 @@ function makeSkillsQuery(skills: Array<{ softDeletedAt?: number; moderationStatu
withIndex: (name: string) => {
if (name !== 'by_active_updated') throw new Error(`unexpected skills index ${name}`)
return {
order: (dir: string) => {
if (dir !== 'asc') throw new Error(`unexpected skills order ${dir}`)
return {
paginate: async () => ({
page: skills,
isDone: true,
continueCursor: null,
pageStatus: null,
splitCursor: null,
}),
}
},
collect: async () => skills,
}
},
}