jason
jason5mo ago

Background work

I think GPT O3 probably gave me a good response, but I want to confirm. There is no waitUntil but can either await it or use:
ctx.scheduler.runAfter(0, internal.analytics.trackUserSignup, {
userId
});
ctx.scheduler.runAfter(0, internal.analytics.trackUserSignup, {
userId
});
Downside here is inefficiency of an extra HTTP request. a waitUntil would be useful, if possible to implement on Convex
14 Replies
jamwt
jamwt5mo ago
There's no extra http request!
jason
jasonOP5mo ago
Sorry, I misspoke. I meant function call, for billing.
Jakov
Jakov5mo ago
Yea just await the scheduler Make sure to await it
erquhart
erquhart5mo ago
Move the functionality from the scheduled function directly into the function you’re calling from, or you can split that functionality out to a shared helper function if you need to run it in both. But I suspect you’re scheduling a mutation from an action here. Since actions can’t directly interact with the database, a separate function call is required no matter what, unless you can turn the whole action into a mutation. Which you can’t if it’s making fetch calls for AI stuff. So the extra call isn’t superfluous, this is things working as designed.
jason
jasonOP5mo ago
@erquhart interestingly, the use case is to send an analytics event to posthog within Convex Better Auth's onCreateUser() callback. I'll likely refactor to await ctx.scheduler.runAfter(0, ...) or just use await my HTTP request for now to keep it simple and see if it's fast enough. But a waitUntil() primitive would be very nice to have to allow fast returns to the user, while keeping the execution context alive until the waitUntil() returns
No description
erquhart
erquhart5mo ago
await ctx.run* is effectively waitUntil()
jason
jasonOP5mo ago
but with an extra function invocation and more overhead code wise to write another internal function It works, but waitUntil would still be handy imo
erquhart
erquhart5mo ago
You keep saying waitUntil, but await ctx.run* would be fine if it didn't add a function invocation to your usage - is that correct? Or is there another aspect here Like, waitUntil could be called something else and still satisfy your goals I'm guessing, so I'm trying to understand the goals specifically
jason
jasonOP5mo ago
yep. the name is irrelevant to my end goal. I'm just referring to it as waitUntil() b/c that's the name of the primitive offering this same behavior on Cloudflare (which is already used in parts of my code).
erquhart
erquhart5mo ago
That's fair. There are fundamental differences between Convex and Cloudflare workers, though. But it sounds like you want the effect that helper functions have, without splitting out helper functions. If so, that's been requested a ton (I asked about it a lot early on myself).
jason
jasonOP5mo ago
Certaintly. But if both use isolates, then it seems reasonable it'd be possible to implement on Convex too
erquhart
erquhart5mo ago
Convex functions running in connection with the database, and queries and mutations in particular being atomic transactions, are the critical difference Actions would be simpler to do this with though I imagine, which is what would apply to your use case here
jason
jasonOP5mo ago
I'll summarize: - keeps current context alive until the promise inside ofwaitUntil() returns. - no extra function call b/c it's the same context - allows to immediately return a value to the user, while doing less essential work like analytics or logging sure but again, that fails at one of the main goals of not being an extra function invocation. It works but is not as ideal as it could be imo
erquhart
erquhart5mo ago
Helper functions are the way to achieve what you're trying to do currently. Which, I know isn't what you're hoping for. But it's the best approach available. I'll also say, fwiw, I stopped looking for the behavior you're describing when I really started growing my backend and began thinking of every Convex function as an api endpoint, which they are. Once you have a lot of them, it becomes an anti-pattern to have a bunch of code written directly in the endpoint, and what we call "helper functions" becomes the standard, just so you can reason about things like access. That's been my experience, anyway. Again, recognizing this isn't what you're asking for. But I'd encourage you to give the helper function approach a shot if you're willing. It feels like boilerplate at first, but it has a lot of advantages architecturally.

Did you find this page helpful?