Just in case it's helpful (if not, no worries), these are the batch timings that I get for 6M rows with the above implementation. It seems that the client buffers the entire result set before passing it on.
The narrow timing here also suggests that any streaming issues in Observable's data tables may be caused by the table implementation, not the MD backend.
(And fwiw, I'm fully aware that this use case is likely unsupported; I'm mostly documenting things for myself)