If you use ordinary READ COMMITTED transactions, they will advance their xmin horizons on each query (and allow old version cleanups) up until their own transaction id but unfortunately not beyond that. For a given table, this is important since that transaction is uncommitted and it might modify the table. If you could make long-running transactions readonly on those specific tables, then you could use a different xmin horizon specifically for those tables. It would require a lot of duplicative bookkeeping in shared memory though. You could probably fake this today by using 2 databases on the same machine and using two-phase-commit+dblink/fdw for cross-database transactions/queries (fdw uses repeatable read in transactions, so it won't allow the xmin to advance).
What I don't want is long running unrelated transactions keeping old tuples alive just it case they're needed. My third suggestion where transactions attempting to read old versions fail is probably better than the first two where isolation gets silently downgraded, since it avoid that problem.