-
Notifications
You must be signed in to change notification settings - Fork 28
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
Try using a unique temp file for vdocs #585
Conversation
Oof, that's a nasty failure case, thanks for finding it! I like the uuid solution a lot, and this is good to merge as far as I'm concerned. One question for our collective pondering: how concerned are we about creating large numbers of temporary files here? If we're getting multiple |
Looks like success 🤞 reported in posit-dev/positron#3792 (comment). I've pointed the other folks here to get the |
For the long term, is it possible to use in-memory virtual documents? Like with a true LSP backend? Do you have context for why the extension is using temp files in this way? In the short term, I definitely would advocate for merging something along the lines of this PR, pending another person or two confirming that this fixes their problem, and releasing the extension ASAP. This has been probably the most pernicious crash type Positron users have run into since we went public! |
Looks like another report of success here: posit-dev/positron#3945 (comment) |
I don't. I wasn't involved in writing that code, so I'm reading it for the first time and figuring it out. It looks like |
We definitely leak files like crazy now, since we never clean up Still super nice to know what the fix is, we just need a way to clean up when the server is done processing the request (which i think isn't straightforward). |
Oh wait, I'm dumb. These are all LSP requests. Meaning we get a reply back from the server when its done. Duh! So when we get the reply we just clean up the temp file. So if we want to keep the tempfile approach I think we can make this work without leaking files. We are even doing this already! In this
And we do call this cleanup hook in a number of places. Here is an example: quarto/apps/vscode/src/lsp/client.ts Line 237 in 97cc659
But what I really like is this quarto/apps/vscode/src/vdoc/vdoc.ts Lines 124 to 132 in 97cc659
We should consider using this everywhere we use a virtual doc right now, and consider taking it a step further as just |
Regarding why we do the tempfile approach. It is possible that some LSP requests require workspace context. So by putting the tempfile in the workspace alongside the qmd (where other R files might also exist), then the LSP has the capabilities to process the request on the tempfile with the context of other R files around it (this is a somewhat educated guess from reading the sources) |
07242ee
to
784f147
Compare
(Sorry I pushed a commit here by mistake, I force-pushed it away) |
Some more observations: As long as a document is opened (in the sense of Cancellation works via a cancellation token that must be checked manually by assistance providers. We currently fail to do that, and instead propagate the assistance request to LSP backends. These backends then are at risk of operating on outdated documents and provide incorrect results or even crash. The workaround taken in this PR is reasonable. It creates a unique document for each request and any further change causes a new unique document to be created. This guarantees that requests operate on an unchanging source and prevents races. That may be the safest way we can implement the bridge between chunks and assistance providers. And if the provider returns results for outdated documents, VSCode will ignore them. So as long as it doesn't cause performance issues, we probably want to keep doing that. However this approach only hides the problem. We should also make an effort to cancel propagation of assistance requests to LSPs, if only to avoid unnecessary traffic with the backends. Here is an example with the completion provider: 99be124
I was looking at this too, should be easy enough: 0eaea03
IIUC this is the "local" code path, currently used for formatting and jump-to-definition. This stores the files alongside the qmd files. But here we are working with the non-local path that stores files in a temp folder. I think files from a virtual document provider would be treated in the same way, and be part of the workspace just like these tempfiles? |
Co-authored-by: Davis Vaughan <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@cscheid If you have time would you mind doing a final review? If not no worries, we've closely looked at it with @DavisVaughan already.
We're aiming to merge before Wednesday end of day so this can get in the next Positron release.
The main thing that we changed in addition to Julia's fix is to make sure that temporary files are now cleaned up consistently and we don't leak any. In particular, we opted for removing the "same content" optimisation so that we don't have to reuse the same tempfile for multiple LSP requests, which would complicate their lifetime/ownership management.
We feel like there is room to do better in the future but for now we wanted to make the simplest possible change.
if (langVdoc.getText() === virtualDoc.content) { | ||
// if its content is identical to what's passed in then just return it | ||
return { uri: langVdoc.uri }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was an optimisation for when multiple LSP requests are performed in a row but the underlying file hasn't changed. We removed it to simplify the lifetime management of the tempfiles. We now always create a new tempfile for each request and each caller is in charge of cleaning up the file (we double-checked that all callers currently do clean them up).
local: boolean | ||
) : Promise<VirtualDocUri> { | ||
): Promise<VirtualDocUri> { | ||
const newVirtualDocUri = (doc: TextDocument) => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
New constructor for a vdoc-uri with a cleanup function.
|
||
export function isLanguageVirtualDoc(langauge: EmbeddedLanguage, uri: Uri) { | ||
return languageVirtualDocs.get(langauge.extension)?.uri.toString() === uri.toString(); | ||
return newVirtualDocUri(doc); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now cleaning up the tempfiles in this code path too.
Previously it would only be deleted manually when a change was detected in the contents.
Another report of success here: posit-dev/positron#4437 (reply in thread) |
Carlos is OOO so let's go with the work as is, thoroughly checked out by Lionel and Davis and confirmed by our couple of Positron users who were having trouble. ✅ |
Maybe addresses a category of problem some users are experiencing in Positron:
No one on the team has been able to observe this problem directly but it is clearly real. @DavisVaughan came up with the hypothesis that maybe the problem is that the
vdoc
tempfiles are getting overwritten by different requests. Quoted from Slack:This PR uses UUIDs to make a whole bunch of different tempfiles, one for each request, rather than one file that could get deleted before the request can be handled. This is maybe overkill but we can have folks get the
.vsix
to try it out.