-
Notifications
You must be signed in to change notification settings - Fork 0
[DO NOT MERGE]: Datasets cache PoC #21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,69 @@ | ||||||||||||||||||||||||||||||||||||||||||
| import { searchDatasets } from "@/lib/queries/dataset"; | ||||||||||||||||||||||||||||||||||||||||||
| import { Dataset } from "@/schemas/dataset.interface"; | ||||||||||||||||||||||||||||||||||||||||||
| import { unstable_cache } from "next/cache"; | ||||||||||||||||||||||||||||||||||||||||||
| import { z } from "zod"; | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| // FIXME: how can we prevent simulateneous cache revalidations | ||||||||||||||||||||||||||||||||||||||||||
| // when a cache revalidation is requested while another is already | ||||||||||||||||||||||||||||||||||||||||||
| // running? Woudln't happen with revalidate set to false | ||||||||||||||||||||||||||||||||||||||||||
| export const getCachedDatasets = unstable_cache( | ||||||||||||||||||||||||||||||||||||||||||
| async () => { | ||||||||||||||||||||||||||||||||||||||||||
| console.log("Revalidating datasets cache: ", new Date().getTime()); | ||||||||||||||||||||||||||||||||||||||||||
| const allDatasets: Dataset[] = []; | ||||||||||||||||||||||||||||||||||||||||||
| const limit = 10; | ||||||||||||||||||||||||||||||||||||||||||
| let page = 0; | ||||||||||||||||||||||||||||||||||||||||||
| while (true) { | ||||||||||||||||||||||||||||||||||||||||||
| const pageDatasets = await searchDatasets({ | ||||||||||||||||||||||||||||||||||||||||||
| limit, | ||||||||||||||||||||||||||||||||||||||||||
| offset: limit * page, | ||||||||||||||||||||||||||||||||||||||||||
| groups: [], | ||||||||||||||||||||||||||||||||||||||||||
| orgs: [], | ||||||||||||||||||||||||||||||||||||||||||
| tags: [], | ||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+16
to
+22
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Constrain cached corpus to datasets Without Apply this diff: const pageDatasets = await searchDatasets({
limit,
offset: limit * page,
groups: [],
orgs: [],
tags: [],
+ type: "dataset",
});📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| if (!pageDatasets?.results?.length) { | ||||||||||||||||||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| allDatasets.push(...pageDatasets.results); | ||||||||||||||||||||||||||||||||||||||||||
| page++; | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| return allDatasets; | ||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||
| ["cached-datasets"], | ||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||
| revalidate: false, // TODO: what happens if the UI triggers a time-based revalidation? | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| export const searchOptionsSchema = z.object({ | ||||||||||||||||||||||||||||||||||||||||||
| limit: z | ||||||||||||||||||||||||||||||||||||||||||
| .preprocess((x) => Number(x), z.number().min(0).max(25)) | ||||||||||||||||||||||||||||||||||||||||||
| .optional() | ||||||||||||||||||||||||||||||||||||||||||
| .default(10), | ||||||||||||||||||||||||||||||||||||||||||
| page: z | ||||||||||||||||||||||||||||||||||||||||||
| .preprocess((x) => Number(x), z.number().min(1)) | ||||||||||||||||||||||||||||||||||||||||||
| .optional() | ||||||||||||||||||||||||||||||||||||||||||
| .default(1), | ||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+39
to
+48
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Zod preprocess breaks defaults; missing params parse as NaN
Apply this diff: export const searchOptionsSchema = z.object({
- limit: z
- .preprocess((x) => Number(x), z.number().min(0).max(25))
- .optional()
- .default(10),
- page: z
- .preprocess((x) => Number(x), z.number().min(1))
- .optional()
- .default(1),
+ limit: z
+ .preprocess((x) => (x === undefined || x === "" ? undefined : Number(x)), z.number().int().min(1).max(25))
+ .optional()
+ .default(10),
+ page: z
+ .preprocess((x) => (x === undefined || x === "" ? undefined : Number(x)), z.number().int().min(1))
+ .optional()
+ .default(1),
});📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| type SearchOptions = z.infer<typeof searchOptionsSchema>; | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| // NOTE: for search, I think we should use a lib like minisearch | ||||||||||||||||||||||||||||||||||||||||||
| // for the FTS, and use a DTO to return results. We could even | ||||||||||||||||||||||||||||||||||||||||||
| // cache this list of datasets DTO. This would reduce data transfer | ||||||||||||||||||||||||||||||||||||||||||
| // and increase performance in the pages that use search | ||||||||||||||||||||||||||||||||||||||||||
| // The search index can be a module-level singleton, it doesn't have | ||||||||||||||||||||||||||||||||||||||||||
| // to be cached | ||||||||||||||||||||||||||||||||||||||||||
| export async function searchCachedDatasets(options: SearchOptions) { | ||||||||||||||||||||||||||||||||||||||||||
| const { page, limit } = options; | ||||||||||||||||||||||||||||||||||||||||||
| const allDatasets = await getCachedDatasets(); | ||||||||||||||||||||||||||||||||||||||||||
| const filteredDatasets = allDatasets; | ||||||||||||||||||||||||||||||||||||||||||
| // NOTE: maybe https://github.com/itemsapi/itemsjs instead of minisearch ? | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| const startIdx = (page - 1) * limit; | ||||||||||||||||||||||||||||||||||||||||||
| const endIdx = startIdx + limit; | ||||||||||||||||||||||||||||||||||||||||||
| const paginatedDatasets = filteredDatasets.slice(startIdx, endIdx); | ||||||||||||||||||||||||||||||||||||||||||
| return { results: paginatedDatasets, count: filteredDatasets.length }; | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,24 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { searchCachedDatasets, searchOptionsSchema } from "@/lib/data"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { NextApiRequest, NextApiResponse } from "next"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { ZodError } from "zod"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export default async function handler( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| req: NextApiRequest, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| res: NextApiResponse | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (req.method === "GET") { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const validatedOptions = searchOptionsSchema.parse(req.query); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const results = await searchCachedDatasets(validatedOptions); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| res.status(200).json(results); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (e) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (e instanceof ZodError) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| res.status(400).json({ message: "Validation Error", errors: e.issues }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+9
to
+19
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add 500 fallback and return after 400 Non‑Zod errors lead to a hanging request. Also return after sending 400 to avoid double responses. Apply this diff: if (req.method === "GET") {
try {
const validatedOptions = searchOptionsSchema.parse(req.query);
const results = await searchCachedDatasets(validatedOptions);
res.status(200).json(results);
} catch (e) {
if (e instanceof ZodError) {
- res.status(400).json({ message: "Validation Error", errors: e.issues });
+ return res
+ .status(400)
+ .json({ message: "Validation Error", errors: e.issues });
}
+ console.error("Cached datasets search failed:", e);
+ return res.status(500).json({ message: "Internal Server Error" });
}
} else {📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| res.setHeader("Allow", ["GET"]); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| res.status(405).end(`Method ${req.method} Not Allowed`); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.