A tile is ~1.2k small parquet objects; sequential PUTs to the bucket are
round-trip-bound (measured 2.5 obj/s, ~8 min per tile — ~78 h for the city
stage). A 16-thread pool per tile turns that into well under a minute; boto3
clients are thread-safe and failures surface via future.result().