Yes, this is how Fly.io networking is intended to work—in general. There’s a distinction between “edge” nodes (Mumbai, in this case) and “worker” (a.k.a. “instance”) ones.
https://community.fly.io/t/what-difference-between-edge-and-instace/8797/2
You control which worker nodes you use, but, by default, all the edge nodes everywhere are turned on for you.
The end user’s own local ISP, plus networks in between, will decide which Fly.io edge is the best choice from their perspective. (This isn’t always minimum latency; it’s also at least a little about minimizing their own costs.) The Fly.io edge then forwards it along to the closest worker, which may well be in a different region.
It looks like there is some ability to tell the platform that you only ever want a single edge region to be used, but I don’t know whether that’s available for Singapore:
https://community.fly.io/t/the-price-of-bandwidth-is-still-confusing/23399/4
Broadly speaking, the default setup is typically good about not crossing entire oceans unnecessarily, but it’s not always going to be optimize down to 10–20ms granularity.
Hope this helps a little!
Aside: The flyio-debug
trick and all-pairs round-trip times tables are handy ways to see this more explicitly…
Aside2: Also note that Anycast is not a broadcast of any kind. Only one of the 30+ potential destinations is chosen. (I.e., it’s not trying both Mumbai and Singapore.)