Repository navigation
[convex] Feedback from building a few example apps on 1.1.0 #74
Description
Activity
Roadmap update now that #75 is released as 2.0.0 — here's how we're sequencing the rest of this issue:
Next PR (in final review now): background-execution QoL
- Item 2 —
onComplete:runBackgroundaccepts a mutation function handle (Workpool-style) plus anonCompleteContextpassthrough; the component's poller invokes it on every terminal state (completed,failed, and the newcancelled), so backends no longer need a second polling loop on top of ours. - Item 4 —
cancelExecution: atomic claim (racing the poller's finish throws instead of overwriting), kills the session so the command actually stops, firesonCompletewithcancelled. - Item 4 —
deleteSandbox404 now marks the recorddestroyed, matching stop/refresh. - Item 4 — configurable polling:
minPollMs/maxPollMsonrunBackground(clamped 250ms–120s), so a fastcargo buildcan resolve in ~0.5s and long jobs can poll lazily. - Item 4 — row hygiene:
purgeExecutions/purgeSandboxes(batched, terminal rows only) plus astatefilter and cursor-paginatedlistSandboxesPaginated— your "how many sandboxes are live" rate-limit check becomeslistSandboxes(ctx, { state: 'started' }).
After that, as its own PR: Item 3 — webhook-driven state sync. The component will ship the HTTP handler + signature verification (mounted via
registerRoutes, secret passed down through component env like the API key), and we're investigating registering the webhook programmatically via Daytona's webhooks API so setup stays dashboard-free.Deferred pending design: binary
readFile/writeFile. It'll land as additive*Bytesvariants (v.bytes()), with Daytona's pre-signed URLs as the story for payloads beyond Convex's function size limits.And yes please on the example repos — three real consumers make a great regression suite. Thanks again Mike, this issue has been the most productive feedback loop of the launch.
Reacted by Mike Cann- Item 2 —
Awesome thanks @mislavivanda, that roadmap sounds great, onComplete and cancelExecution especially will let me rip out a bunch of polling code from these. Here are the three example repos:
- Mini Lovable: https://github.com/get-convex/convex-daytona-app-builder
- Chat with your CSV (Python): https://github.com/get-convex/convex-daytona-csv-chat
- Rust compiler error fixer: https://github.com/get-convex/convex-daytona-rust-fixer
They are still on the pre-2.0.0 version so they might need a little nudge to upgrade, let me know if you hit anything weird. Cheers!
Hey @mikecann, all of the items from the issue are now implemented and published in the newest
2.1.0version. I think the opt-in option for state sync with Daytona webhooks will be really useful.Thank you for your great feedback and suggestions which resulted in significant improvements of this component and will ultimately leave to better experience & usability for Convex users.
Closing the issue and feel free to lift new FR in the future if you notice anything else that might be useful!
Hey @mislavivanda, thanks again for turning #69 around so fast,
runBackgroundis super nice!For the video I had my agents build three little example apps on top of the component (a mini Lovable, a "chat with your CSV" Python one, and a Rust one that keeps fixing compiler errors until it builds). They all work really well, but a few things came up along the way, roughly in order of how much they bit us:
1. The API key gets stored in the scheduled function args
runBackgroundandpollExecutionpassconfig(includingapiKey) toctx.scheduler.runAfter, so the Daytona key gets written in plain text to the component's_scheduled_functionstable on every poll, and Convex keeps those records for 7 days after they run.It also means that if you rotate the key while a background command is running, the polls keep using the old one, fail 3 times and the execution gets marked as failed. The cleanup
deleteSessionuses the old key too, so the command actually keeps running in the sandbox.Not sure what the nicest fix is given components can't read the app's env vars, but maybe the client could pass a function handle (e.g. to an internal query that returns the config) instead of the key itself, so only the handle ends up in the scheduled args?
2. No completion callback for
runBackgroundThe reactive execution row is great for the UI, but the backend has no way to know when the command has finished. All three apps ended up writing their own scheduled "check the row and reschedule" loop on top of the component's poller. Something like an
onComplete: internal.myModule.buildFinishedfunction handle (similar to Workpool) would remove that second layer.3. Stale sandbox state
Same as option 3 on #69: once Daytona auto-stops a sandbox the record still says
started, which made "how many sandboxes are live" a guess in our rate limiting.4. Smaller ones
deleteSandboxthrows if Daytona already auto-deleted the sandbox (404), and the record never gets markeddestroyed.stopSandboxandrefreshSandboxalready handle the 404, so it would be nice if delete did too.listSandboxesonly takesuserKeyandlimit(max 500, no pagination), so there's no filtering by state or label. There's also no way to delete olddestroyedsandbox or execution rows, so they pile up forever.readFile/writeFileare UTF-8 strings only, so we had to save matplotlib charts as SVG. A bytes variant (v.bytes()) would help for images and other binaries.cargo buildalways shows up about 1.2s later, and long jobs only update their logs every 10s.deleteSessionwhen it gives up, so maybe exposing that as acancelExecutionwouldn't be too bad?The example repos are public now if they're useful for testing:
Cheers!