Skip to content

x/tools/gopls: GOPACKAGESDRIVER calls happen at intervals that make the results incorrect #59625

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
JamyDev opened this issue Apr 13, 2023 · 20 comments
Labels
gopls Issues related to the Go language server, gopls. Tools This label describes issues relating to any tools in the x/tools repository.
Milestone

Comments

@JamyDev
Copy link

JamyDev commented Apr 13, 2023

What version of Go are you using (go version)?

$ go version
1.20.3

Does this issue reproduce with the latest release?

Yes

What operating system and processor architecture are you using (go env)?

go env Output
$ go env
GO111MODULE="off"
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/user/.cache/go-build"
GOENV="/home/user/.config/go/env"
GOEXE=""
GOEXPERIMENT="nocoverageredesign,nounified"
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOINSECURE=""
GOMODCACHE="/home/user/go-code/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="linux"
GOPATH="/home/user/go-code"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/home/user/.cache/bazel/_bazel_jamy/b97476d719d716accead0f2d5b93104f/external/go_sdk_linux_amd64"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/home/user/.cache/bazel/_bazel_jamy/b97476d719d716accead0f2d5b93104f/external/go_sdk_linux_amd64/pkg/tool/linux_amd64"
GOVCS=""
GOVERSION="go1.20.3 X:nounified,nocoverageredesign"
GCCGO="gccgo"
GOAMD64="v1"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD=""
GOWORK=""
CGO_CFLAGS="-O2 -g"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-O2 -g"
CGO_FFLAGS="-O2 -g"
CGO_LDFLAGS="-O2 -g"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -Wl,--no-gc-sections -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build2135155060=/tmp/go-build -gno-record-gcc-switches"

What did you do?

With GOPACKAGESDRIVER enabled, adding an import (whether by inline writing code and accepting an import suggestion; or manually adding it to the imports section) will trigger a query to the packages driver. However, this trigger will happen prior to the file being saved, yet the packagesdriver will only get a filename to load packages for.

This triggers a bit of a race condition in build systems like Bazel:

  1. Current imports in file.go are a, b, c
  2. I add import d
  3. GoPLS immediately tries to look up package info from the driver using the filename ($ pkgdrvr file=file.go)
  4. Since the file isn't saved at this point in time, the packagesdriver will only see imports a, b, c and thus only return info for those 3
  5. GoPLS gets incomplete results back and flags import d as bad.
  6. Even after save, there is no re-trigger of the packagesdriver, which means this bad imports stays bad until after the imports are changed again; or the language server is restarted.

What did you expect to see?

Since the packagesdriver works with files on disk (in the general case) we should not query the packagesdriver until the IDE has committed the changes on disk.

Alternatively we can extend the packages driver call to query directly for the information of the imported package, rather than indirectly querying for the file the import is added to.

What did you see instead?

Package import resolution fails until language server restart.

I will try to get a project where we can repro this situation in.

@gopherbot gopherbot added Tools This label describes issues relating to any tools in the x/tools repository. gopls Issues related to the Go language server, gopls. labels Apr 13, 2023
@gopherbot gopherbot added this to the Unreleased milestone Apr 13, 2023
@hyangah
Copy link
Contributor

hyangah commented Apr 14, 2023

Can you share which gopackagesdriver you are using.
The default gopackage driver go list handles this case by using overlay.
https://pkg.go.dev/golang.org/x/tools/go/packages#Config

Is it possible to make your gopackagesdriver recognize the overlay info?

@golang/tools-team I think we need documentation on gopackagesdriver #34341

@JamyDev
Copy link
Author

JamyDev commented Apr 14, 2023

We use the rules_go one. I'll have a look at overlay info and see how to support it in the rules_go driver

@suzmue suzmue modified the milestones: Unreleased, gopls/unplanned Apr 20, 2023
@findleyr
Copy link
Member

We discussed this a bit more in our team meeting, and decided it would probably be very difficult to support this in the bazel driver (based on our understanding of bazel).

So we'll leave this open: it would be nice to fix, but unlikely to get prioritized any time soon. The work to implement this would be approximately:

  • accumulate invalidations in cache.snapshot.clone while the user types.
  • only invalidate and reload packages when the user saves all files

...but this will be very tricky to implement, and even after implemented may have to a subtly confusing UX.

@li-jin-gou
Copy link

@findleyr Hello, What is the status of this support programme?
We had a similar problem when we implemented a packagedriver ourselves and he would dynamically generate code to return package information. but because of caching issues, there is no way to get the latest generated code. Is there a recommended solution? Apart from rebooting

@findleyr
Copy link
Member

@li-jin-gou I think this issue may have been superseded by #68002. I plan to implement #68002 this year. It is currently targetting the v0.17.0 release, which is tentatively planned for October.

@ViolaPioggia
Copy link

@findleyr Hello, What is the status of this support programme? We had a similar problem when we implemented a packagedriver ourselves and he would dynamically generate code to return package information. but because of caching issues, there is no way to get the latest generated code. Is there a recommended solution? Apart from rebooting

I have a similar problem to him. After my local dependencies using gopackagesdriver are changed, I hope gopls can get the latest data in time. Is there any gopls command that can achieve this? Or is there any other better way?

@li-jin-gou
Copy link

Thanks, until then we can move forward with the programme of trying to restart gopls. @findleyr

@JamyDev
Copy link
Author

JamyDev commented Aug 23, 2024

FWIW at Uber we did 2 things to fix this:

  1. Support overlays on our internal driver. The overlay content contains the newly added import which fixes the initial issue (assuming you have some way of resolving said import without your dependency manager).
  2. Added a "reload Imports" codelens above the imports section. Clicking this invalidates the target in our cache, and then slightly modifies the imports (adds a space at the front of the first import and removes it right after). The imports modification triggers gopls to request the imports from the packages driver.
    @li-jin-gou @ViolaPioggia

@li-jin-gou
Copy link

FWIW at Uber we did 2 things to fix this:

  1. Support overlays on our internal driver. The overlay content contains the newly added import which fixes the initial issue (assuming you have some way of resolving said import without your dependency manager).
  2. Added a "reload Imports" codelens above the imports section. Clicking this invalidates the target in our cache, and then slightly modifies the imports (adds a space at the front of the first import and removes it right after). The imports modification triggers gopls to request the imports from the packages driver.

thanks

@Skyenought
Copy link

FWIW at Uber we did 2 things to fix this:

  1. Support overlays on our internal driver. The overlay content contains the newly added import which fixes the initial issue (assuming you have some way of resolving said import without your dependency manager).
  2. Added a "reload Imports" codelens above the imports section. Clicking this invalidates the target in our cache, and then slightly modifies the imports (adds a space at the front of the first import and removes it right after). The imports modification triggers gopls to request the imports from the packages driver.
    @li-jin-gou @ViolaPioggia

sorry sir. In our case, the content of *packages.DriverResponse is unchanged, but the content of the file it maps to has changed.
So the IDE is not aware of the fact that the prompts are no longer correct due to a change in the file

  {
    "ID": "service1/hello",
    "Name": "hello",
    "PkgPath": "rgo/service1/kitex_gen/hello",
    "GoFiles": [
      "/root/cache/service1/hello/hello.go"
    ],
    "CompiledGoFiles": [
      "/root/cache/service1/hello/hello.go"
    ],
    "Imports": {
      "context": "context",
      "fmt": "fmt"
    }
  },

In fact it really doesn't describe whether or not the specifics of the file change. Have you ever had this problem? @JamyDev

@li-jin-gou
Copy link

Dependencies may have been added or the original dependency code may have changed.

@rv32ima
Copy link

rv32ima commented Dec 29, 2024

I'm able to reproduce this. We use Bazel as our build system and this has been one of the more annoying bits. I've recorded what this looks like, and how you can get around it by deleting another import, saving, and then putting it back. That triggers packages.Load to be called again on the file with the updated metadata.

Video is too big to upload to GitHub: https://share.cleanshot.com/YSjRRv3D

@rv32ima
Copy link

rv32ima commented Dec 29, 2024

I delved into this, and made some minor adjustments to gopls in order to have it reload the "state of the world" whenever a Bazel-related file was being touched. It's quite brute-force & dumb, but it seemingly works for us: https://github.com/devzero-inc/tools

@findleyr
Copy link
Member

findleyr commented Jan 3, 2025

@ellie-idb thanks for looking into this.

If it works, I'd be totally fine with configuration that customizes the set of 'workspace file' patterns, with the default value of []string{"go.mod", "go.work"}. That's a good idea. We could perhaps define it as follows:

// (in gopls/internal/settings.BuildOptions)

// BuildFiles configures the set of regular expressions that match files defining the logical build of the current workspace.
// Any on-disk change to a file matching these expressions causes the workspace to be reloaded.
//
// This setting need only be customized in environments that set GOPACKAGESDRIVER.
BuildFiles []string

One of our major concerns with extending explicit support for bazel is the lack of test coverage (and we have ~zero budget to add such coverage). If your proposed configuration allows us to address this issue without adding any explicit logic for bazel, that largely mitigates this concern.

I can help you land this in gopls. To start, could you update your patchset with proposed changes that avoid bazel-specific logic? Then I can help you with testing; we have extremely limited coverage for GOPACKAGESDRIVER, but should be able to exercise this logic using the fake driver that actually calls go list.

@rv32ima
Copy link

rv32ima commented Jan 3, 2025

@findleyr
I took the liberty of calling it WorkspaceFiles rather than BuildFiles, but I've gone ahead and implemented it as you said -- avoiding any specific Bazel-related logic and making it a parameter that takes in LSP-y globs without modifying the API surface too much as well as trying to match any existing behavior. I had to re-arrange some code here and there, but otherwise, there shouldn't be anything too objectionable in the PR.

One note is that I didn't want to introduce any new dependencies as part of this work, so I implemented the file matching using the golang.org/x/tools/gopls/internal/test/integration/fake/glob package... which, I'm not satisfied about, but nevertheless, seemed like the best option here. I've tested this locally on our monorepo and it works just fine once configured properly with Bazel like so:

// in .vscode/settings.json
    "gopls": {
        "build.workspaceFiles": [
            "**/*.{work,mod}",
            "**/BUILD",
            "**/WORKSPACE",
            "**/*.{bzl,bazel}",
        ],
    },

. Let me know what you think! Happy to hop on a call or something to get this landed.

@findleyr
Copy link
Member

findleyr commented Jan 3, 2025

I took the liberty of calling it WorkspaceFiles rather than BuildFiles

Sounds good, I was actually undecided between those names.

Are you able to send your patch as a CL in Gerrit, so that we can continue discussion in CL comments? (see https://go.dev/doc/contribute if you are new to contributing).

@rv32ima
Copy link

rv32ima commented Jan 3, 2025

@gopherbot
Copy link
Contributor

Change https://go.dev/cl/640076 mentions this issue: gopls: add WorkspaceFiles option

gopherbot pushed a commit to golang/tools that referenced this issue Jan 22, 2025
WorkspaceFiles allows an end-user to specify a set of files which, when
modified, will trigger a full reload of any views currently open in a
session. This is especially important for users who use custom
GOPACKAGESDRIVERS, as previously, you were forced to restart the
language server in order to get up-to-date diagnostics in some certain
instances.

For golang/go#59625

Change-Id: Iba7a6137cb0b88a59318217a9a28d079100192a4
Reviewed-on: https://go-review.googlesource.com/c/tools/+/640076
Auto-Submit: Robert Findley <[email protected]>
Reviewed-by: Alan Donovan <[email protected]>
LUCI-TryBot-Result: Go LUCI <[email protected]>
Reviewed-by: Robert Findley <[email protected]>
@gopherbot
Copy link
Contributor

Change https://go.dev/cl/647295 mentions this issue: gopls/doc/release: document the new workspaceFiles option

gopherbot pushed a commit to golang/tools that referenced this issue Feb 6, 2025
Add release notes for the new workspaceFiles option. Also, clean up
existing release notes a bit.

Updates golang/go#59625

Change-Id: I8969a48b2fddd9c60a77c038deb9090bd2e9eb98
Reviewed-on: https://go-review.googlesource.com/c/tools/+/647295
LUCI-TryBot-Result: Go LUCI <[email protected]>
Reviewed-by: Alan Donovan <[email protected]>
Auto-Submit: Robert Findley <[email protected]>
@rv32ima
Copy link

rv32ima commented Mar 7, 2025

hey folks -- it looks like my fix for this change has made it out in v0.18.0 (see: https://github.com/golang/tools/releases/tag/gopls%2Fv0.18.0). to enable, you'll need to modify your LSP settings to add the following lines: https://github.com/ellie-idb/bazel-go-starter-repo/blob/533cf015072f75a1369981aec21675fdd9b7b29a/.vscode/settings.json#L20-L26. please let me know if you still have issues with out of date metadata after this!

cc @JamyDev @li-jin-gou

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
gopls Issues related to the Go language server, gopls. Tools This label describes issues relating to any tools in the x/tools repository.
Projects
None yet
Development

No branches or pull requests

9 participants