Things were getting too laggy and so this week I've been focusing upon getting timeline updates as quick as possible.
Previously the conversion from json formatted ActivityPub posts to html was done on demand. That is, whenever you select a timeline. But in some cases, such as Announce activities or posts where the actor needs to be downloaded, this can involve lookups which slow things down. So now the html is generated at the time when a post is received by the inbox and then cached. When you select a timeline the relevant cached html files are then merely looked up and delivered, without any additional overhead. This also reduces strain on the CPU since any lookups only need to be performed once.
I've also added indexes for timelines, and this improves performance a lot. Previously the index was just the directory listing in descending order of status number, but creating this listing when you have a lot of posts in the inbox directory can take time. Instead it's better to just make an index file which is prepended to whenever a new post arrives. when generating a timeline the system then only needs to read the first few index entries, and doesn't even need to load the entire index file into memory.
The combination of cached html and indexes now makes timeline delivery about as fast as it's likely to get. So the only limit is really just the speed of your server disk storage and how much internet bandwidth you have.