The only case where you can reasonably say that there are "two versions of
the same package" in a build, is if they are in different major versions of
the same module¹, say example.com/foo/v2 and example.com/foo/v3.
In that context, you have control over both versions. You can guarantee
that they use the same mutex, by having them refer to each other. That is,
you would implement v2 in terms of v3, that way ensuring that even if some
code still imports v2, they still ultimately use the v3 mutex.
However, there is a problem: You can only implement v2 in terms of v3, once
that exists. So you might introduce v3 while v2.23 is out and then have to
introduce v2.24 to be implemented in terms of v3. But some clients might
still import an v2.23. If those builds than *also* add v3, you end up with
two mutexes.
The way around that is to have both of those refer to a third package,
which contains the mutex. So you would have example.com/internal/mutex
(which lives in its own module example.com/internal) which contains a mutex
and have example.com/foo/v2@v2.24 import that to refer to the mutex. You
can then wait until all importers have upgraded to v2.24. And when you then
build example.com/foo/v3, it also imports example.com/internal/mutex. That
way, all builds use a single mutex.
Of course, there is still a rest risk of clients not upgrading from v2.23
at all. But unfortunately, if you can't *somehow* prepare the earlier
version and rely on that preparation to propagate, you are out of luck, I
believe.

I don't believe there is a trick to statically enforce that two packages
can not be imported at the same time (neither in the general case, nor in
the special case that they are in two major versions of the same module).
In a way, that's by design. It would be undesirable, for the import of one
package to be able to break the import of a different package. Because if
you *could* get into a situation where package A and package B are mutually
exclusive, you could have a module dependency graph E->C->A, E->D->B and
then the author of module E could do nothing to fix their build (as they
can not force the authors of either module C or module D to change their
imports, in a timely manner). Preventing these kinds of situations was a
major design goal of Go modules. Which is why it's possible to use two
major versions of a module in the same build in the first place.

You could potentially dynamically enforce that there are no two major
versions of the same module used in one build. A simple (somewhat limited)
way would be to use `runtime/debug.BuildInfo` to check which modules are
included in an `init` function and panic/exit if there is a conflict. But I
would strongly warn against that. Because as I said, making this impossible
statically was done for a reason and those reasons still apply if you do it
dynamically - just that it's even more frustrating, because you will only
notice *after* building and potentially deploying the binary.

So, I think the real advice is: Don't. Do not rely on the assumption, that
the same mutex is used. If nothing else, you can't prevent someone from
forking your module and someone else to end up with both the original in
the fork in the build. And if your case would break, that would break as
well.

[1] Some may argue that this is not "the same package", as different major
versions can be considered different modules and because Semantic Import
Versioning is intentionally part of the module design. However, I think in
the context of the question, that's still reasonably "the same package".

On Sun, 11 May 2025 at 06:45, Jason E. Aten <j.e.a...@gmail.com> wrote:

> Is there a way to insure I've only got one version
> of a package in a build?  I need to make sure
> a single mutex is used by all goroutines.
>
> Detailed back story is here:
> https://github.com/golang/go/issues/73665
>
> --
> You received this message because you are subscribed to the Google Groups
> "golang-nuts" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to golang-nuts+unsubscr...@googlegroups.com.
> To view this discussion visit
> https://groups.google.com/d/msgid/golang-nuts/bdc40509-6b71-469e-82ce-c5fb4a64211bn%40googlegroups.com
> <https://groups.google.com/d/msgid/golang-nuts/bdc40509-6b71-469e-82ce-c5fb4a64211bn%40googlegroups.com?utm_medium=email&utm_source=footer>
> .
>

-- 
You received this message because you are subscribed to the Google Groups 
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to golang-nuts+unsubscr...@googlegroups.com.
To view this discussion visit 
https://groups.google.com/d/msgid/golang-nuts/CAEkBMfHAjwz%2BcNsiP-5cGbM1NVxMc0Muazzj2dxqXH4McJwYHg%40mail.gmail.com.

Reply via email to