3rd party functions
Does Convex support "warm starts" for serverless external Handler functions (https://docs.convex.dev/using/external-functions)? What does the latency generally look like for these functions and is there any way to improve it?
25 Replies
Hi! So, these functions are run by third parties, like Vercel and Netlify. Do you mean warm starts of those functions on the 3rd parties, or warm starts with an existing connection to Convex, or something else?
(Also, we're adding support for running these kinds of functions directly on Convex right now, so the story around this type of work should be much improved soon!)
and with regards to queries and mutations in Convex, these are warm and intended to be as low-latency as possible
I think however you're referring to external functions, which you run on third parties like @jamwt mentioned. Whether or not these support warm starts will depend on the provider. e.g., Vercel has some docs here: https://vercel.com/guides/how-can-i-improve-serverless-function-lambda-cold-start-performance-on-vercel
Vercel Documentation
How can I improve serverless function (lambda) cold start performan...
This guide will help you improve the performance of your lambda functions and understand how to determine if the latency increase is from a cold start.
yup I was talking about external functions. I now realize that they are run on another service like Netlify / Vercel
so does this mean that query / mutation functions in Convex can't have any "side effects" like hitting another API (in our case we'd want to hit the Twilio API)
that's correct -- for now! we're actively working on versions of functions with side effects that we intend to roll out soon
it's still beneficial for most of your pure database functions to be side-effect free for caching, and consistency, conflict resolution, lots of good database-y reasons. but we want to add good support for functions with side effects for when you need them!
got it, what about webhooks? for example, we might want a convex mutation to trigger when Twilio sends a POST request to a certain URL ... do we have to route through some self-hosted express server to do that (on Vercel or Netlify)?
we also have plans to allow you to hook up routes to controllers for things like webhooks, exactly.
that's probably a little way out but part of the architectural vision
got it, but right now you have to make two hops, go through some express server hosted through vercel and then that server calls a convex mutation on the convex server?
today, that is true. yep.
got it. i think for our usecase webhooks are a necessity and functions with side-effects are almost a necessity, would love to know the timeline on when those would be out 😃
cool. the functions with side effects are already underway, so expect to have some first version of that within a few weeks. @presley can probably say more. webhooks, those still need to be prioritized / designed, so I would expect those to be at least a couple of months out. the team will have to dig in more. but it's great to hear more about your use case. please dm me if you're up to jump on a call sometime so we can hear about what you have in mind, it might help us prioritize things!
Yeah, our priorities are 1. functions with side effects 2. webhooks 3. other cool stuff around that will make Convex more self contained platform. 1 and 2 should ship by end of year.
got it. the use-case I envision for functions with side effects is running the side effect at the end, after the mutation commits. Do you plan to support that use-case, and will it still be fast taking into mind the "caching, consistency, conflict resolution" considerations you mentioned?
yep, running a function (with side effects) after a mutation commits is definitely compatible with our database philosophy.
it'd be the same idea as if the client received the response and then initiated the function call, but without the roundtrip in the middle.
we're still ironing out what this would look like (do you "defer" a function in the middle of a mutation? or maybe write to a table and then a function gets triggered on that? etc.) but would love your thoughts.
honestly to me it seems like the simplest API is having handler objects just like you have query and mutation objects. handlers don't have access to the convex DB or anything by default but they can call queries and mutations, and queries and mutations also return their "results" directly to the handlers as well as writing to the db
ah yep, that's exactly what we're building first for functions! functions can call into queries + mutations but not vice versa.
i'm just trying to avoid the latency of communicating between vercel -> convex functions and then convex functions -> db update -> listener on vercel
but I think the current design is fine otherwise
cool!
when a function calls a query or mutation does it get the result "immediately" or does it have to listen to the db somehow?
nope, it's just like an RPC call -- something like
await convex.query(queryName)(args)
similar to calling into a query (or mutation) via the HTTP APIgot it
so suppose you have a mutation function like the one in your docs for sending a message if it is more than 10 characters:
would there be any way to determine that the mutation "succeeded" in sending the message, vs failed?
for example, could we get the return value of the mutation?
I think this would be pretty key for running mutations
yep, if your function is calling into a mutation with
result
will just be "ok" in the success path. the goal is to make this feel pretty close to just simple function calls.ok perfect! I think this v1 would be all we need for side effects in functions, let me know when it's live and would love to try it out, even as a beta feature
this all sounds great! the only thing (as in all CS) to be aware of is if you end a "handler" with a side effect, rather than a mutation (a commit point), you can't use convex to guarantee at-least-once of whatever the side effect was
but as long as that doesn't matter for the semantics of your app, all good
ah i see, why would you run the code in handlers twice?
suppose you had a dummy mutation that inserted a user into a DB if the user didn't already exist ... and it returned 'new_user_created" if you had the user in the DB and 'user_already_exists' if the user wasn't already in the DB
and then we had a handler that called this mutation and then called an external service to sign the user up for something if the result was 'new_user_created'
would we call the external service twice?
ah I think jamie's point is that the mutation would execute exactly once, but then the subsequent call to the external service could fail. if the function doesn't retry, it'll be at-most-once execution. if it does retry, it's possible for the external call to happen twice which may not be safe for the external service. but, in either case that's up to you within your function, and we won't run it twice for you automatically.
Following up on this older thread: we now support scheduling actions - which are serverless functions that support side-effects. So you could have transactional logic in a
mutation
and schedule follow-up work that can have side-effects, like hitting external services. Hopefully you won't need vercel serverless functions anymore! https://blog.convex.dev/announcing-convex-0-8-0/The Convex Blog
Announcing Convex 0.8.0
0.8.0 brings scheduled functions and dashboard filters to Convex!
Also @Nikhil Cheerla we've had functions with side-effects for a while now (we call them "actions") and we just launched http endpoint support - so it's easy to point webhooks at Convex
https://blog.convex.dev/announcing-convex-0-9-0/
The Convex Blog
Announcing Convex 0.9.0
HTTP endpoints are here! Convex 0.9.0 extends the platform by allowing you to build your HTTP API right within Convex.