Skip to content
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

Temporarily serialize requests in the LSP, and fix the bugs that doing so revealed #3370

Merged
merged 10 commits into from
Oct 11, 2024

Conversation

mcy
Copy link
Member

@mcy mcy commented Oct 4, 2024

Fixes #3362, #3359, #3361.

Previously, the LSP contained a somewhat fussy concurrency model, where files had to be carefully locked and unlocked (with runtime enforcement) to avoid deadlocks. This is necessary so that parsing and resolution does not hang the LSP for particularly long: you want to avoid a situation where a user opens a file that takes several seconds to load, which is a realistic possibility.

This does not hang the editor itself, but prevents go-to-definition from working on already-loaded files while the new file is loading. Already with only needing to go three or four levels deep, as in the protos in buf-registry, causes a noticeable pause. I have not profiled it, but I suspect it's because the CLI is not optimized for memoization, so the memoization that I'm doing probably duplicates work other parts of the CLI are doing.

However, the current solution was understood to be temporary, since Josh and I are designing a new query system for incremental compilation, which would replace the current system. Also, the current system is quite painful to debug, despite the many steps I have taken to enforce locking invariants.

Therefore, given that:

  1. The current solution is going to be replaced,
  2. The current solution is a velocity speed bump, because it's tricky to debug,
  3. The LSP is useable with a less-than-ideal user experience until (1),

it makes sense to switch to a global lock to serialize all requests until (1). Removing (2) will make it easier to fix "low hanging" bugs in the LSP, instead of blocking on (1) happening. If we start pushing the LSP to users before (1), we should beware of (3).

As a result of switching to serialized requests, #3362 disappears in a puff of "this problem no longer exists". It also revealed that the way dependency paths were produced by GetImportableImageFileInfos was subtly incorrect if the input file path was inside of the cache. Fixing this resolved #3359. Because we now we have crisp information about where imports come from, it is trivial to resolve #3361.

Copy link
Contributor

github-actions bot commented Oct 4, 2024

The latest Buf updates on your PR. Results from workflow Buf CI / buf (pull_request).

BuildFormatLintBreakingUpdated (UTC)
✅ passed✅ passed✅ passed✅ passedOct 11, 2024, 2:55 PM

@@ -129,6 +130,7 @@ type Controller interface {
defaultMessageEncoding buffetch.MessageEncoding,
options ...FunctionOption,
) error
GetWKTBucket(ctx context.Context) (storage.ReadBucket, error)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shouldn't be part of the Controller really - instead, expose bufcli.newWKTStore as public bufcli.NewWKTStore (ie just s/n/N/) and call this function in buflsp to make a bufwktstore.Store.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment on lines 344 to 345
// If this is an external file, it will be in the cache and therefore
// finding imports via lsp.findImportable() will not be possible.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not entirely sure I follow this logic -- so a file we may be checking may be a dependency file, and therefore the URI would be to a file in the cache. But I'm not sure why that would then mean we would not be able to call controller.GetImportableFileInfos? It appears to me that we are basically replicating that logic in findImportable (resolving the workspace using the controller, walking the files, and adding WKTs)...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GetImportableFileInfos and findImportable will both produce "truncated" paths when looking up the workspace of a cached file, presumably because it does not have a buf.yaml. I think trying to fix that is beyond the scope of this PR, so I'm working around it. A comment will appear soon explaining it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I think I might know what's going on. In the case of the cache, we do not have a way to show the root of the workspace (e.g. there are no configuration files), so when resolving the workspace for the given file path, it is truncating it to that directory. There may be a way to fix this by special-casing paths within the cache, since those are known modules.

@bufdev
Copy link
Member

bufdev commented Oct 8, 2024

What's the status on this one?

@mcy
Copy link
Member Author

mcy commented Oct 8, 2024

I'm currently stuck debugging the issue Doria pointed out (insofar that I think there's a way I'm using workspaces wrong but docs are a bit paltry), but unfortunately the logging migration appears to have broken debugging for the LSP (and broken some other things along with it? still triaging). Not sure I can give an ETA on that.

@bufdev
Copy link
Member

bufdev commented Oct 8, 2024

I'm not sure how the logging would have impacted it, but if you can point out specifics happy to help. The changes were largely superficial, however.

@mcy
Copy link
Member Author

mcy commented Oct 10, 2024

The biggest issue was that the function https://github.com/bufbuild/buf/pull/3370/files#diff-9fde706d21f072dfdf6f3fc98fc5dd6035d8aca46905dc8d6e819cf3cdde4f5bR335 was deleted with no replacement. slog does not natively know how to log arbitrary types, and passing such values into slog.Any does not cause anything to actually be logged. There's a lot of places where symbols are logged to track whether symbol resolution is working correctly and it took me a while to figure out why logging wasn't coming out right.

Also there were several unfortunate merge conflicts because the logging upgrade touched every log statement in the LSP, so merging main introduced additional bugs I had to track down while fixing logging.

@mcy mcy merged commit 71e170e into main Oct 11, 2024
10 checks passed
@mcy mcy deleted the mcy/lsp-big-lock branch October 11, 2024 15:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
3 participants