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

Move RazorWorkspaceListener to AsyncBatchingWorkQueue #10480

Merged
merged 12 commits into from
Jun 14, 2024

Conversation

ryzngard
Copy link
Contributor

In Linux + VS Code when creating a new project we're seeing work get dropped from the queue that would result in up to date information being written to disk. Looking at the logs + the bin file it's noticeable that the correct project information isn't there, and that results in Razor behaving poorly by not having the correct files in the correct projects, and often times lacking the accurate amount of taghelpers.

Switching to AsyncBatchingWorkQueue seems to improve reliability here. I also added more trace logging to help identify when information is being written, when files are moving, etc. A try/catch was added to make sure we log in case of an exception as well.

There are still issues on Linux, but this solves part of them.

@ryzngard ryzngard requested a review from a team as a code owner June 12, 2024 21:11
@ryzngard ryzngard marked this pull request as draft June 12, 2024 21:13
@ryzngard ryzngard marked this pull request as draft June 12, 2024 21:13
@ryzngard ryzngard marked this pull request as ready for review June 12, 2024 21:23
Copy link
Member

@DustinCampbell DustinCampbell left a comment

Choose a reason for hiding this comment

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

Putting everything in a batch is probably much better for reliability than what we were doing before. There are still going to be disk contention problems though -- it won't cure them completely. In my mind, the solutions for that would be one of:

  1. Process synchronization when writing reading from disk via a named mutex shared between the process this is loaded in and rzls. However, we should ensure that there's one mutex per Roslyn LSP/Razor LSP process pair to avoid sharing the same mutex between different VS Code instances.
  2. Avoid the disk altogether by serializing to a named MemoryMapFile and somehow passing that name to the Razor LSP, which would delete it after deserializing.
  3. Wait for co-hosting. 😄

break;
default:
break;
}
}

private void RemoveProject(Project? project)
Copy link
Member

Choose a reason for hiding this comment

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

It's a little worrisome that we drop project removes on the floor. We definitely watch for deletions of the project.razor.bin file in the language server. If a project is deleted from disk, do we leave the language server in a descent state? Or, does that project continue to live there with all of its documents and references, responding to Rename, Component search, completion, etc.?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This never handled removing the file, it just stopped the queue. I'm not actually sure what handles cleanup. Since we're working on _workspace.CurrentSolution I'm guessing it would be safe to just delete the file when a project is removed?

That's new, and maybe it's already done somewhere else? Not sure

Copy link
Contributor Author

Choose a reason for hiding this comment

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

One thought:

We could enqueue and update for the removed ProjectId and in the ABWQ work method we could clean up the file if it exists and the project is no longer in the solution?

Copy link
Contributor

Choose a reason for hiding this comment

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

This was probably unnecessary originally, but yeah I was just trying to remove the scheduler. We'd never get any more updates for the project anyway, so could have ignored removes then too.

Not sure I like the idea of removing the file though. I think in VS Code we are more likely to be processing requests before the file is written, so it does serve more like an offline cache, even if it is one that won't be being updated any more.

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, I agree that this is probably OK. I saw all of the red and notice that all it did was get rid of the scheduler before. With #10475 I think VS Code will start reading in .bin even sooner, so you're right that VS Code will use it more as a cache.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Test failures lead to one thing remove did that I didn't notice: we only queue work for items that have been reported with NotifyDynamicFile. This PR breaks that. I'm adding remove back to fix

@ryzngard
Copy link
Contributor Author

Putting everything in a batch is probably much better for reliability than what we were doing before. There are still going to be disk contention problems though

Yea. Disk contention is attempted to be reduced by writing to a temp file and then moving it to the final file. This is done in

var fileInfo = new FileInfo(configurationFilePath);
if (fileInfo.Exists)
{
fileInfo.Delete();
}
File.Move(tempFileInfo.FullName, configurationFilePath);

}

public void Dispose()
// Protected for testing
#pragma warning disable RS0016 // Add public types and members to the declared API
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Suppressing until I can figure out if it's user error or analyzer error.

Copy link
Contributor

Choose a reason for hiding this comment

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

Seems like neither. A protected method of a public type is part of the public API. I'd either remove the suppression, or create a TestAccessor so it doesn't need to be protected.

Copy link
Member

Choose a reason for hiding this comment

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

Is this a case where private protected is needed?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes! That's exactly what I was thinking. I just got my newfangled things mixed up

break;
default:
break;
}
}

private void RemoveProject(Project? project)
Copy link
Contributor

Choose a reason for hiding this comment

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

This was probably unnecessary originally, but yeah I was just trying to remove the scheduler. We'd never get any more updates for the project anyway, so could have ignored removes then too.

Not sure I like the idea of removing the file though. I think in VS Code we are more likely to be processing requests before the file is written, so it does serve more like an offline cache, even if it is one that won't be being updated any more.

}

public void Dispose()
// Protected for testing
#pragma warning disable RS0016 // Add public types and members to the declared API
Copy link
Contributor

Choose a reason for hiding this comment

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

Seems like neither. A protected method of a public type is part of the public API. I'd either remove the suppression, or create a TestAccessor so it doesn't need to be protected.

Copy link
Member

@DustinCampbell DustinCampbell left a comment

Choose a reason for hiding this comment

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

With question about removes answered, this looks good to me!

@ryzngard ryzngard enabled auto-merge (squash) June 13, 2024 20:25
@ryzngard ryzngard merged commit 5457702 into dotnet:main Jun 14, 2024
12 checks passed
@dotnet-policy-service dotnet-policy-service bot added this to the Next milestone Jun 14, 2024
@ryzngard ryzngard deleted the projectinfo_logging branch June 14, 2024 05:06
ryzngard added a commit to dotnet/roslyn that referenced this pull request Jun 17, 2024
Two major changes being introduced:

dotnet/razor#10480 moves our project change listener to AsyncBatchingWorkQueue to improve stability
dotnet/razor#10475 rearchitects our project syncing. Expected to have no impact on VS Code
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants