Description
Fresh off the press, the upcoming Go 1.23 release will ship with some annoying changes to the linker.
Here's the full issue: golang/go#67401
As a summary, the Go authors are concerned by the prevalent use of go:linkname
throughout the Go ecosystem to access the internals of the runtime. They intend to hamper the usage of this feature in the upcoming release by adding a strict check, so that only runtime symbols that have been explicitly exported inside the runtime can be accessed externally via go:linkname
. To prevent further breakage of the ecosystem, they've scanned the top N most popular Go packages and whitelisted their go:linkname
symbol dependencies so they can continue to be linked externally for the foreseeable future.
Unfortunately for us, Vitess makes some mild use of go:linkname
and it's not in the top N packages that have been scanned: although Vitess is plenty popular as an OSS project, their scan measures popularity by number of dependant packages. As a "top level" application, there's very few projects that have vitessio/vitess
as a go.mod
dependency. So we're screwed this way.
On the other hand, as a top level package, we have access to a new flag in ldflags
that allows us (and will always allow, as it'll become part of the Go compatibility guarantees) to disable the whitelisted export checks and access the Go internals indiscriminately. We can always choose to build Vitess with this flag, and we may need to do so in the short term as soon as we upgrade to Go 1.23.
Regardless, I think it'd be a good idea to create a tracking issue to enumerate all our go:linkname
dependencies and eventually remove all of them until we can compile Vitess with the strict linker checks in Go 1.23.
-
//go:linkname strhash runtime.strhash
(hack/runtime.go
): this function is now exported in the new hash packages in the stdlib so there’s no reason to continue to link to it this way. -
//go:linkname roundupsize runtime.roundupsize
(hack/runtime.go
): this function is kinda important and has not been whitelisted in Go 1.23 for external usage. We use it as part of our caching infrastructure to calculate very accurate measurements for how much memory do cached objects occupy. Thesizeof
of a Go struct is not the actual amount of memory it occupies, because all structs are rounded up to the GC’s allocation sizes; by accessing this internal method, we can calculate the exact memory usage of each allocation in the cache. There are two options here:- copy this code into Vitess and vendor it; it’s not that much code, and it hasn’t changed since Go 1.0, so that’s always an option
- just stop trying to calculate memory usage accurately and assume
Sizeof(OUR STRUCT)
is the size it’ll occupy in memory. This will under-report memory usage, but how much of a big deal is it?
-
//go:linkname Atof64 strconv.atof64
,//go:linkname Atof32 strconv.atof32
(hack/runtime.go
): these arestrconv
methods that parse a floating point value and return how many characters were parsed. The public methods exported in thestrconv
package will error out if there’s any trailing data after the floating point value, but this is not MySQL’s behavior, so we need access to this internal method. This doesn’t look like it’ll be whitelisted, but it’s very little code: we should vendor it in theparse
package. Upon further review, these methods implicitly support parsing hexadecimal floating point numbers, something which MySQL does not, so we might as well vendor them and remove this extra functionality that doesn’t match MySQL’s. -
//go:linkname ParseFloatPrefix strconv.parseFloatPrefix
(go/hack/compat.go
): this is not used anywhere, we get to just remove it ✨ -
//go:linkname writeBarrier runtime.writeBarrier
,//go:linkname atomicwb runtime.atomicwb
(atomic2/atomic128.go
): these methods are used to implement our own 128-bit atomic integers. They’ve actually been whitelisted already, so we can depend on them indefinitely. Furthermore, we’ll only need to keep this implementation temporarily, as 128 bit atomics have been approved for an upcoming Go release, they’re just missing an implementation. As soon as they’re in the standard library we can remove our implementation. -
//go:linkname sync_runtime_Semacquire sync.runtime_Semacquire
,//go:linkname sync_runtime_Semrelease sync.runtime_Semrelease
(smartconnpool/sema_norace.go
): these are an important but not crucial performance optimization for our connection pool implementation. The methods appear to be whitelisted in the Go standard library, but they use a specific linkage name, so it’s not clear to me whether they can be accessed by Vitess or only by the explicit packages where they’re exported. Regardless, the usage of semaphores can be replaced withsync.WaitGroup
, which is slightly less efficient. -
//go:linkname DisableProtoBufRandomness google.golang.org/protobuf/internal/detrand.Disable
(hack/detrand.go
): Oh boy. You can see the story behind this linkname in the file where’s it’s created:Lines 23 to 41 in 3773563
- This will never be whitelisted because it’s not part of the standard library. There are two options here:
- Have one of our interns finish Remove Golden Tests from the test suite #8162, which is a lot of busywork but it’s not hard at all to do.
- Have a soft fork of the
protobuf
package that changes one line of code, which is annoying but also very little work.