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

Backup file system provider can not do buffered write #193151

Closed
rebornix opened this issue Sep 14, 2023 · 14 comments
Closed

Backup file system provider can not do buffered write #193151

rebornix opened this issue Sep 14, 2023 · 14 comments
Assignees
Labels
author-verification-requested Issues potentially verifiable by issue author bug Issue identified by VS Code Team member as probable bug file-io File I/O freeze-slow-crash-leak VS Code crashing, performance, freeze and memory leak issues important Issue identified as high-priority insiders-released Patch has been released in VS Code Insiders verified Verification succeeded

Comments

@rebornix
Copy link
Member

rebornix commented Sep 14, 2023

Does this issue occur when all extensions are disabled?: Yes/No

  • VS Code Version: 1.83-Insiders
  • OS Version: macOS

I was verifying #192970 and found that large file piece tree buffers are still merged before write. Turns out that backup is always doing doWriteUnbuffered since it's using FileUserDataProvider, which doesn't have FileOpenReadWriteClose capability so it always runs

await this.doWriteUnbuffered(provider, resource, options, bufferOrReadableOrStreamOrBufferedStream);
}

and unfortunately unbuffered write always concatenates all buffers first

buffer = readableToBuffer(bufferOrReadableOrStreamOrBufferedStream);

image
@rebornix rebornix added the freeze-slow-crash-leak VS Code crashing, performance, freeze and memory leak issues label Sep 14, 2023
@rebornix
Copy link
Member Author

One interesting behavior I found is sometimes backup happens right after I press cmd+s to save, maybe the delayed backup kicks in right before I save, but it feels weird to have both a backup and save operation at the same time.

@bpasero bpasero added the important Issue identified as high-priority label Sep 15, 2023
@bpasero bpasero added this to the September 2023 milestone Sep 15, 2023
@bpasero bpasero added the file-io File I/O label Sep 15, 2023
@bpasero
Copy link
Member

bpasero commented Sep 15, 2023

Good catch, but I think this is simply something that was forgotten when introducing the file user data provider. Since the underlying disk file system provider supports this, I see no reason why the file user data provider could not support this.

@sandy081 can you check?

@bpasero
Copy link
Member

bpasero commented Sep 15, 2023

Looks like this was an explicit decision in 87bf1d8#diff-28073e52c2a7e44ac456dfb32c4f0794985cb4a4bcc7c0d99a033e8224fff52cR25 and probably done to enforce atomic writes.

I think we may be fine to bring back that capability. The file service is already optimising the calls to the provider in a way that writeFile is being called when the buffer is not too large (3*64kb I think):

// optimization: if the provider has unbuffered write capability and the data
// to write is not a buffer, we consume up to 3 chunks and try to write the data
// unbuffered to reduce the overhead. If the stream or readable has more data
// to provide we continue to write buffered.
let bufferOrReadableOrStreamOrBufferedStream: VSBuffer | VSBufferReadable | VSBufferReadableStream | VSBufferReadableBufferedStream;
if (hasReadWriteCapability(provider) && !(bufferOrReadableOrStream instanceof VSBuffer)) {
if (isReadableStream(bufferOrReadableOrStream)) {
const bufferedStream = await peekStream(bufferOrReadableOrStream, 3);
if (bufferedStream.ended) {
bufferOrReadableOrStreamOrBufferedStream = VSBuffer.concat(bufferedStream.buffer);
} else {
bufferOrReadableOrStreamOrBufferedStream = bufferedStream;
}
} else {
bufferOrReadableOrStreamOrBufferedStream = peekReadable(bufferOrReadableOrStream, data => VSBuffer.concat(data), 3);
}
} else {
bufferOrReadableOrStreamOrBufferedStream = bufferOrReadableOrStream;
}

On top of that: if any component comes with a buffer for writing, we never stream:

async write(resource: URI, value: string | ITextSnapshot, options?: IWriteTextFileOptions): Promise<IFileStatWithMetadata> {
const readable = await this.getEncodedReadable(resource, value, options);
if (options?.writeElevated && this.elevatedFileService.isSupported(resource)) {
return this.elevatedFileService.writeFileElevated(resource, readable, options);
}
return this.fileService.writeFile(resource, readable, options);
}

Which I would assume is the case for all our settings when written to programmatically.

@sandy081
Copy link
Member

@bpasero Does it also need FileUserDataProvider to support FileSystemProviderCapabilities.FileOpenReadWriteClose ?

@bpasero
Copy link
Member

bpasero commented Sep 15, 2023

@sandy081 yes, that is what the IFileService will probe for before deciding which method to use. Since yours does not declare it, all writes are without streaming.

rebornix added a commit that referenced this issue Sep 15, 2023
@rebornix
Copy link
Member Author

I experimented adding the FileOpenReadWriteClose capability to the FileUserDataProvider #193228, with this backup streams again and no longer blocks the UI. Feel free to discard my PR if it's in the wrong direction

@bpasero
Copy link
Member

bpasero commented Sep 18, 2023

I wonder if a better approach for this would be to drop the FileUserDataProvider and simply expose a method on IFileService to set options for certain paths.

Basically this method would call into IFileService instead of requiring to subclass the disk file system provider:

private updateAtomicWritesResources(): void {
this.atomicWritesResources.clear();
for (const profile of this.userDataProfilesService.profiles) {
this.atomicWritesResources.add(profile.settingsResource);
this.atomicWritesResources.add(profile.keybindingsResource);
this.atomicWritesResources.add(profile.tasksResource);
this.atomicWritesResources.add(profile.extensionsResource);
}
}

@sandy081
Copy link
Member

I liked that. Does it mean we should not use vscode-userdata scheme in Desktop?

@bpasero
Copy link
Member

bpasero commented Sep 18, 2023

@sandy081 I would not suggest to change anything with regards to URI or scheme, just simply add a way on the IFileService to enforce custom options when writing for a set of Uri. Its a bit of a hack, but I find the custom file system provider to cause more issues. I would assume you can drop it then.

@sandy081
Copy link
Member

I am wondering it is not straight forward to use disk FSP without changing the scheme. The main purpose of vscode-userdata fsp is to handle the scheme, for eg., watching on user data scheme and getting change events on the same scheme. We should also adopt the disk FSP not to call uri.fsPath directly which I saw using it here

protected toFilePath(resource: URI): string {
return normalize(resource.fsPath);
}

@bpasero
Copy link
Member

bpasero commented Sep 18, 2023

@sandy081 how about this then:

I think then we get the best of all worlds:

  • we don't change schemes
  • we don't change file system providers around fsPath handling
  • the decision whether to use atomic write or not moves into the IFileService based on a whithlist and the new API

@sandy081
Copy link
Member

Sounds good 👍

@bpasero bpasero added the bug Issue identified by VS Code Team member as probable bug label Sep 20, 2023
@bpasero
Copy link
Member

bpasero commented Sep 20, 2023

@sandy081 I went with a slightly different approach: since the file user data provider is used in many contexts, it makes more sense to keep the decision about which resources should get atomic read/write inside the provider. As such, I added new methods to enforce atomic read, write or delete: #193228

@vscodenpa vscodenpa added unreleased Patch has not yet been released in VS Code Insiders insiders-released Patch has been released in VS Code Insiders and removed unreleased Patch has not yet been released in VS Code Insiders labels Sep 20, 2023
@bpasero bpasero added the author-verification-requested Issues potentially verifiable by issue author label Sep 22, 2023
@vscodenpa
Copy link

This bug has been fixed in the latest release of VS Code Insiders!

@rebornix, you can help us out by commenting /verified if things are now working as expected.

If things still don't seem right, please ensure you're on version c72447e of Insiders (today's or later - you can use Help: About in the command palette to check), and leave a comment letting us know what isn't working as expected.

Happy Coding!

@rebornix rebornix added the verified Verification succeeded label Sep 27, 2023
@github-actions github-actions bot locked and limited conversation to collaborators Nov 4, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
author-verification-requested Issues potentially verifiable by issue author bug Issue identified by VS Code Team member as probable bug file-io File I/O freeze-slow-crash-leak VS Code crashing, performance, freeze and memory leak issues important Issue identified as high-priority insiders-released Patch has been released in VS Code Insiders verified Verification succeeded
Projects
None yet
Development

No branches or pull requests

4 participants