-
Notifications
You must be signed in to change notification settings - Fork 324
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
Turn export service into a Vercel serverless function #3095
Conversation
This pull request is being automatically deployed with Vercel (learn more). nusmods-website – ./website🔍 Inspect: https://vercel.com/nusmodifications/nusmods-website/dhkenhm8x nusmods-export – ./export🔍 Inspect: https://vercel.com/nusmodifications/nusmods-export/r3ub9qji4 |
Codecov Report
@@ Coverage Diff @@
## master #3095 +/- ##
=======================================
Coverage 55.58% 55.58%
=======================================
Files 255 255
Lines 5311 5311
Branches 1218 1218
=======================================
Hits 2952 2952
Misses 2359 2359 Continue to review full report at Codecov.
|
Deployment preview for |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good as a direct port, but you might want to restructure the code a bit more since the code looks like it has a lot of copy-pasted chunks from the old Koa middleware code
export/api/export/image.ts
Outdated
const dataCandidate = JSON.parse(request.query.data as never); | ||
validateExportData(dataCandidate); | ||
data = dataCandidate; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is that as never
cast? Isn't this the same as
data = JSON.parse(request.query.data);
validateExportData(data);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
export/api/export/image.ts
Outdated
const handler: NowApiHandler = async (request, response) => { | ||
try { | ||
await main(request, response); | ||
} catch (e) { | ||
const eventId = Sentry.captureException(e.original || e); | ||
|
||
console.error(e); | ||
|
||
if (e instanceof HttpError) { | ||
if (e.code === 422) { | ||
response.status(422).send(render422()); | ||
return; | ||
} | ||
} | ||
response.status(500).send(render500(eventId)); | ||
} | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should definitely be lifted to a higher order function, and we can probably deduplicate most of the two main
functions - right now they are very similar
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm okay.
I originally decided against that because I thought we'll want the handler file itself to perform all the response sending and error throwing, so that it's easier to see what's sent back to the client and also to trace the thrown error and where they're caught. So almost all the duplicated code just throws errors and creates responses.
But I drafted a hof and I think it'll still be easy to trace, so let's do that:
function makeHandler<T>(
validateData: (request: NowRequest) => T,
performExport: (page: Page, data: T, response: NowResponse) => void,
): NowApiHandler {
// ...
}
@@ -286,6 +291,15 @@ | |||
dependencies: | |||
"@types/node" "*" | |||
|
|||
"@vercel/[email protected]": |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Man, vercel node bundling it's own TypeScript is super awkward, especially when it diverge from our own version. It seems they forked ts-node for some reason https://github.com/vercel/vercel/blob/master/packages/now-node/src/typescript.ts
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm this could be problematic if they use their version of TS in prod
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, thankfully they don't.
From the build log (internal-only):
...
22:02:42.582 Using TypeScript 4.1.2 (local user-provided)
...
export/api/export/image.ts
Outdated
if (e.message.includes('ERR_CONNECTION_REFUSED')) { | ||
throw new HttpError( | ||
500, | ||
`Could not open the page located at process.env.PAGE (${url}). Try opening it in your browser?`, | ||
e, | ||
); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this just for local development? If so a local env check + logger log might be better
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not necessarily, if the function was wrongly configured in prod or if nusmods.com goes down for some reason this will be shown too. But it sounds like we can improve this error message so that it'll make more sense in prod
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm going to leave this message as is because I expect this to only be encountered if the function isn't set up properly. nusmods.com shouldn't ever have downtime unless Vercel goes down, and if that ever happens the serverless functions will probably be dead too.
@ZhangYiJiang Yeah, there's quite a lot of copy pasted code in this PR 😆 I think apart from the handler code duplication discussed above, the other big copy paste is the render.ts -> render-serverless.ts file. I decided to fork it so that our handlers don't import I think part of this quick and dirty approach is also just to deploy it to prod and see how well it functions (pun not intended, but ha), before we really clean it up for real. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fair - let's go for the MVP then and see how well this works. We should also think of some way of testing this in prod just to see if anything comes up, maybe route a small amount of traffic to this
I think we don’t need progressive rollout for this one. Since the new Vercel-hosted /website will be deployed by a DNS change, we can just change the DNS record back if export (or website) fails. Should be pretty painless. Also, yolo :D |
public/styles.css is necessary for the static 404.html.
3c93b58
to
071d895
Compare
Rebased on master and added |
Context
Stacked on #3092. Part of #3098, work to dedockerize NUSMods to streamline our deployment processes.
Observations
Cold startup time can take a while (~6s?), but I think it's well worth it.
No docs, but I think we should just update all the docs at once go once we've removed Docker.
Not sure if local dev environment is working. I couldn't get
vercel dev
to work locally, but I think that may just be because I'm using WSL. We could just fix this in a future PR.The /export project has quite a bit of code that's not used, specifically the PM2 deployment and ability to load timetable data from the filesystem. With the move to serverless, all the router stuff will also not be needed anymore. I've left all the old code untouched in case this serverless stuff doesn't work out.
If we do decide to keep this serverless setup for good, we'll want to consider inlining the /export project into /website. This will allow us to have a working export button in every deploy 😍
Biggest risks
Test URLs
TODO after this PR