GmCapsule is starting to feel more robust and stable after a week of bug fixing and fine-tuning. The open internet definitely helped expose a few glitches!
Initially I was surprised to see what looked like a massive memory leak, but after a few days of malloc tracing and poking around, the most likely explanation was a combination of an overly high number of worker threads paired with Python's garbage collector taking its time to free things up.
I've now reduced the number of workers to eight on skyjake.fi, which is still a bit high for the amount of traffic, and the RAM level is holding steady. It's still about 28 MB of RAM per thread, which seems greatly influenced by the 20-40 MB fontpacks I'm serving on the capsule. Loss of precise control over memory is definitely a drawback of using a nice high-level language like Python.
It would be worthwhile to reduce the worker count a bit. I could rely on asynchronous I/O for sockets and let the worker threads concentrate on CGI processes and extension modules. Currently each worker is exclusively serving a single client, but I think many simple file transfers could easily be grouped into a single thread.
The other serious problem was dealing with misbehaving clients. Every now and then, one would connect and get a worker thread stuck with no data being transferred either way. Eventually I had to manually set all the client sockets to timeout mode (apparently not doable via pyOpenSSL) and redo the send and receive methods to account for any shenanigans. Stalled connections are now terminated after 10+ seconds.
Yet another issue was that sending large files would spike CPU use to 100%. With the sockets now in nonblocking mode, it was retrying the send way too often. The fix was to add some `select`s to wait until the socket is ready for more data.
On a more general level, I note that writing a server that survives real-world traffic isn't quite so simple. At this point the code I had in the original GmGitView is pretty much completely rewritten. Granted, I'm no expert in socket programming, but not a novice either. Still, making a simple Gemini client seems easier than making a server.
For documenting GmCapsule, I've been trying to do things correctly according to Python conventions, i.e., using docstrings and Sphinx.
It's pretty nice that one can get a full user manual with `help(gmcapsule)` in the Python REPL. Not having used reStructuredText much before, it's been a jarring experience compared to Markdown and to Gemtext in particular. The reST syntax is awkward and full of little gotchas like things breaking down if your indentation is off by one space character. There's a ton of directives for various purposes. Of course, all of this enables writing nicely formatted and detailed technical documentation, but it sure isn't a very pleasant experience.
I'm still trying to figure out a nice way to convert the documentation from reST+Sphinx to Gemtext. One option is to generate Markdown and trim it down to Gemtext. Plain text output is always an option, too, but some structure would be good...
Anyway, I've put a draft HTML version of the documentation here on etc.skyjake.fi:
π 2022-02-01
CC-BY-SA 4.0
The original Gemtext version of this page can be accessed with a Gemini client: gemini://skyjake.fi/gemlog/2022-02_first-week-on-gmcapsule.gmi