Do we have any plans around persistent connections and in general persistent data in basic-webserver?
For example, as I am thinking about improving the sqlite api, one thing that would be nice is being able to initialize a prepared statement once and then reload the same prepared statement on every request. That removes the need to repeatedly pass around and parse strings.
The same could be wanted from roc-pg with tcp connections.
Do we already have a plan of how we want to support things like this?
vaguely yeah
it's the same idea as what I think we should do for "streams" (aka wrappers around file descriptors)
basic idea is that in Roc it's represented as an opaque wrapper around a Box {}
but the critical aspect is that you can only get one of those things from the host
and the host allocates that box in a separate arena from the rest of the heap
so that when it gets passed to roc_dealloc
you can check its address range and see if it's in the special arena, and if so, go look up its address in a hash to figure out what cleanup to do
so the general theme in both cases is "platform gives you a value that is associated in the host with some persistent state that needs to be cleaned up when there are no more references to it"
such as an open file or an open tcp connection
separately, if we want to have basic-webserver support a concept of "run this initialization thing once, and then that gives you a value that can be handed off to every request" - unfortunately I think that requires passing records of functions to the host, which @Folkert de Vries and I have not succeeded in getting working yet, even when Box
ing the functions
because I think we'd need to do something like giving the host separate init
and handleRequest
functions in the same record, so they can share type variables
e.g.
{
init : {} -> Task state [],
handleReq : Request, state -> Task Response [],
}
So I understand the platform side of the lifetimes, but how can something be kept alive across many requests.
Ex. examples/sqlite3.roc
has a query "SELECT id, task FROM todos WHERE status = :status;"
.
For best performance, the goal would be to prepare that query once per thread.
Then on every request have roc be able to get the Box {}
for the prepared query (preferably without using the string at all).
oh I guess maybe it could work if you did something like Model
So it is more a connection pool question than a connection lifetime question.
Brendan Hansknecht said:
how can something be kept alive across many requests
[...]
For best performance, the goal would be to prepare that query once per thread.
Then on every request have roc be able to get theBox {}
for the prepared query (preferably without using the string at all).
yeah I think that's where the init
concept comes in
well, that wouldn't understand pooling I guess
I guess we could introduce a generic "pooling" concept
(that is, basic-webserver could)
well, that wouldn't understand pooling I guess
You could init per thread and it would at least be decent. Maybe a tad wasteful, but functional.
where you can define a Pool a
that knows how to hand things out
Quick aside: I don't think Box {}
works in roc currently. At least not fully. I was originally using it for some of my ffi stuff instead of U64
. Was hitting weird unification errors.
Need to try and build minimal repros at some point.
For Box {}
. Is the thought that I would actually have Box SomePlatformType
? So you have to throw whatever connection/prepared stmt/etc type into a box. Just don't tell roc about the type inside.
That said, I would assume that roc shouldn't actually refcount a box of a zero sized type like {}
. Like it should just never allocate it or anything. But maybe this is useful in practice, so we just Box {}
to be a box of something unknown to roc and still have it refcount and attempt to free the thing.
hm maybe!
the thought was that for a file descriptor, Box I32
would be worse than (I32, Box {})
because now you have to chase a pointer to access the fd
That's fair. With (I32, Box {})
, there is still 8 bytes randomly on the heap, but it is only accessed for refcounting.
yeah and I assume if you ask for this it’s because you’re planning to pass it multiple times to things
Last updated: Jul 06 2025 at 12:14 UTC