aiohttp.TCPConnector (with limit argument) vs asyncio.Semaphore for limiting the number of concurrent connections

Is there a preferred option?

Yes, see below:

will the aiohttp internals lock me down to 100 concurrent connections implicitly?

Yes, the default value of 100 will lock you down, unless you specify another limit. You can see it in the source here:

Are they (roughly) equal in terms of performance?

No (but the difference in performance should be negligible), since aiohttp.TCPConnector checks for available connections anyway, wether or not it is surrounded by a Semaphore, using a Semaphore here would be just unnecessary overhead.

How do I handle (preferably retry x times) coros that threw an error?

I don't believe there is a standard way to do so, but one solution would be to wrap your calls in a method like this:

async def retry_requests(...):
    for i in range(5):
            return (await session.get(...)
        except aiohttp.ClientResponseError: