This is situational, I think. My work mostly involves applications that do significant back-end processing for most requests, and my SSR is always done using a framework that has pre-compiled code doing the rendering rather than an interpreted language. (Perl and C#.) This combination adds a lot of pre-render computing and optimized rendering, which adds up to SSR being a good choice.
I'm not sure what that says about when CSR would be a good choice. If your requests don't do much back-end processing, but still have a long (500ms?) response time, that seems like you're doing something wrong rather than an opportunity to use CSR. Maybe you've chosen a poorly-performing rendering framework. Maybe you're trying to render too-large a page (which would be even more of a problem client-side.)
Let's say you've got a request that does SSR and takes 500ms, with 450ms of that time spent processing the request and 50ms spent rendering the response. If you switch to CSR, you still have to wait 450ms to process the request, and you've got to serialize the response data (eg: render it to a more concise format than html) which is going to take some of that 50ms you're trying to save. So, where is the blocking you're talking about? How does CSR make it go away?
What you wrote sounds like you're describing a singleton that handles all rendering for all requests, and can only handle one request at a time. If that's the case, your framework is a toy and you need to ditch it for something that can handle multiple concurrent requests independently of each other.
> SSR throughput of your server is significantly less than CSR throughput. For react in particular, the throughput impact is extremely large. ReactDOMServer.renderToString is a synchronous CPU bound call, which holds the event loop, which means the server will not be able to process any other request till ReactDOMServer.renderToString completes. Let’s say that it takes you 500ms to SSR your page, that means you can at most do at most 2 requests per second. BIG CONSIDERATION