Skip to content

Commit

Permalink
Pre-warm search
Browse files Browse the repository at this point in the history
  • Loading branch information
sawyerh committed Jul 31, 2023
1 parent cb6aa13 commit 8eaef9c
Show file tree
Hide file tree
Showing 8 changed files with 62 additions and 41 deletions.
9 changes: 9 additions & 0 deletions aws/ai/handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,15 @@
tracer = Tracer()


@app.get("/wake")
def get_wake():
"""
Wake up the function from any cold start
"""

return {"message": "I'm ready!"}


@app.get("/search", compress=True)
@tracer.capture_method
def get_search():
Expand Down
2 changes: 0 additions & 2 deletions aws/ai/poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 6 additions & 6 deletions web/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"devDependencies": {
"@types/gtag.js": "^0.0.12",
"@types/node": "18.16.20",
"@types/react": "^18.2.14",
"@types/react": "^18.2.17",
"autoprefixer": "^10.4.14",
"postcss": "^8.4.21",
"typescript": "^5.1.6"
Expand Down
31 changes: 10 additions & 21 deletions web/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { request } from "helpers/request";

import "server-only";

const AI_URL = process.env.AI_URL;
const FIREBASE_API_URL = process.env.FIREBASE_API_URL;

// Volume covers or titles are often modified after initial import,
Expand All @@ -17,48 +16,38 @@ const VOLUME_REVALIDATION_TIME = 60 * 10; // 10 minutes
* Send a request to a serverless HTTP endpoint in Firebase.
* @example firebaseRequest('volumes')
*/
async function firebaseRequest(route: string, options?: RequestInit) {
return request(`${FIREBASE_API_URL}/${route}`, options);
async function firebaseRequest<TResponse>(route: string, options?: RequestInit) {
return request<TResponse>(`${FIREBASE_API_URL}/${route}`, options);
}

export async function getVolumes() {
const { data } = await firebaseRequest("volumes", {
const { data } = await firebaseRequest<{ data: Volume[] }>("volumes", {
next: {
revalidate: VOLUME_REVALIDATION_TIME,
},
});

return { volumes: data as Volume[] };
return { volumes: data };
}

export async function getVolume(volumeId: string) {
const volume = await firebaseRequest(`volumes/${volumeId}`, {
const volume = await firebaseRequest<Volume>(`volumes/${volumeId}`, {
next: {
revalidate: VOLUME_REVALIDATION_TIME,
},
});

return volume as Volume;
return volume;
}

export async function getHighlights(volumeId: string) {
const { data } = await firebaseRequest(`highlights?volume=${volumeId}`);
const { data } = await firebaseRequest<{ data: Highlight[] }>(`highlights?volume=${volumeId}`);

return data as Highlight[];
return data;
}

export async function getHighlight(highlightId: string) {
const highlight = await firebaseRequest(`highlights/${highlightId}`);
const highlight = await firebaseRequest<Highlight>(`highlights/${highlightId}`);

return highlight as Highlight;
}

export async function search(query: string) {
const { data } = await request(`${AI_URL}/search?query=${query}`, {
next: {
revalidate: 60 * 60 * 24, // 24 hours
},
});

return data as SearchResult[];
return highlight;
}
31 changes: 26 additions & 5 deletions web/src/app/api/search/route.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import { request } from "helpers/request";
import { NextRequest, NextResponse } from "next/server";

import { search } from "api";
const AI_URL = process.env.AI_URL;

export async function GET(request: NextRequest) {
const query = request.nextUrl.searchParams.get("query");
/**
* Perform a search
*/
export async function GET(req: NextRequest) {
const query = req.nextUrl.searchParams.get("query");

if (!query) {
return NextResponse.json(
Expand All @@ -15,6 +19,23 @@ export async function GET(request: NextRequest) {
return NextResponse.json({ error: "Sawyer says No" }, { status: 400 });
}

const results = await search(query);
return NextResponse.json(results);
const { data } = await request<{ data: SearchResult[]}>(`${AI_URL}/search?query=${query}`, {
next: {
revalidate: 60 * 60 * 24, // 24 hours
},
});
return NextResponse.json(data);
}

/**
* The API is a serverless function, and benefits from being "warmed up" before
* a real request comes in.
*/
export async function PUT() {
await request(`${AI_URL}/wake}`, {
next: {
revalidate: 60 * 30 // 30 minutes
},
});
return NextResponse.json({});
}
13 changes: 9 additions & 4 deletions web/src/components/AI.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ function useDialog(ref: React.MutableRefObject<HTMLDialogElement | null>) {
ref.current?.showModal();
lockBodyScroll();
trackEvent("opened-ai-dialog");

// Wake the function up to reduce the cold start time
request(`/api/search`, { method: "PUT" }).catch(console.error);
};

const hide = () => {
Expand Down Expand Up @@ -89,7 +92,7 @@ function useDialog(ref: React.MutableRefObject<HTMLDialogElement | null>) {
return () => ref.current?.removeEventListener("close", unlockBodyScroll);
}, [ref, unlockBodyScroll]);

return { hide, show };
return { hide, show, isOpen: ref.current?.open };
}

/**
Expand All @@ -111,9 +114,8 @@ function SearchingIndicator() {

return (
<p className="text-shadow-sm text-white">
{loadingInterval === 1 && "Warming up"}
{loadingInterval === 2 && "Searching"}
{loadingInterval >= 3 && "Almost there. Searching"}
{loadingInterval === 1 && "Searching"}
{loadingInterval >= 2 && "Almost there. Searching"}
&hellip;
</p>
);
Expand All @@ -135,6 +137,9 @@ const SearchDialog = forwardRef(function SearchDialog(
refetchOnWindowFocus: false,
});

/**
* Dialog backdrop click handler
*/
const handleDialogClick = (e: React.MouseEvent<HTMLDialogElement>) => {
if (e.target === e.currentTarget) {
props.hide();
Expand Down
3 changes: 1 addition & 2 deletions web/src/helpers/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
* Wrapper around `fetch` that returns the JSON body.
* @example request('https://example.com/api/v1/users')
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export async function request<TResponse = any>(
export async function request<TResponse = unknown>(
path: string,
options?: RequestInit,
) {
Expand Down

1 comment on commit 8eaef9c

@vercel
Copy link

@vercel vercel bot commented on 8eaef9c Jul 31, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.