-
Notifications
You must be signed in to change notification settings - Fork 2.5k
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
Use binary message RPC protocol for plugin API #11261
Conversation
Performance TestingThis PR greatly improves the performance of FileSystem calls from within a plugin/VS Code extension. To test this with have used a simply VS Code extension that offers to commands to write/read file of different sample sizes to the workspace and logs the execution time in the console. The source code is available here: https://github.com/eclipsesource/theia/tree/json-rpc-performance-test-extension BenchmarksWe used the extension to measure performance in browser and electron context on:
|
3a83b92
to
28009a3
Compare
I've done a little testing, and I noticed that I get a ton of cancellation error stack traces in the console
Uncaught Error: There is no document for breakpointinput:vs.editor.ICodeEditor%3A4
Error: There is no document for breakpointinput:vs.editor.ICodeEditor%3A4 Monaco silently accepts cancellation if it detects that it is a cancellation. I think the proper layering would be to catch our own cancellation error and to translate it into a VS Code Cancellation Error at some appropriate place. see https://github.com/microsoft/vscode/blob/f52952332d14de7517cb22b002ef1352ecdee889/src/vs/base/common/errors.ts#L149 |
Every once in a while, completion just seems to either fail or take a long time. I can't repeat nor do I have a handle on what's wrong yet. |
Turns out, the completion doesn't really fail, but it kind of looks like it's waiting for something: when I just let the cursor sit there, the completion does not complete. As soon as I move the cursor around, I get the completion. |
Thanks for testing this change @tsmaeder.
Good catch! I double checked with the "old" plugin rpc protocol implementation and it indeed throws a VS Code Cancellation Error whereas the new protocol didn't:
I have adapted the new protocol accordingly and we now should throw the expected Cancellation Error. In addition, I reverted another "over-optimiatization" that prematurely rejected the response of a canceled request. I did a little testing and did not encounter cancellation error stack traces anymore.
I could not observe this behavior anymore. Maybe it got fixed with the cancellation error fixes? Could you please retest this? Thanks! |
There is a problem with the completion in the PR: I've made two videos, first the behavior in master: and now in the PR Both videos are mad in master, on the source code of Theia and with the browser example, using Firefox. Completion seems slower and erratic in timing: sometimes leaving the cursor where you clicked before completion seems to block the completion somehow, but eventually the completion appears. To me it feels as if we were "crossing the streams". Is it possible that cancellations and multiple requests might be blocking or interfering with each other? If I look at what's changed with respect to master, what I'm seeing is that we're using a different multiplexing strategy for routing to multiple services and message batching has changed, as well. I think it would be good to have some configurable tracing in the code to find out what's going on. We just need to make sure we're not sending it over the wire with the remote log infrastructure again ;-) |
I wanted to try out this PR to run benchmarks, but plugins don't seem to work anymore. I am running Windows 10. |
@paul-marechal how did you resolve the conflicts? @tortmayr could you, please? ;-) |
Sure I can resolve the conflicts and retest it under Windows. |
@tsmaeder I just pulled the branch as is and built it, I didn't think I had to rebase myself? |
edit: Obsolete, see new comment. |
@paul-marechal Because @tortmayr is on vacation until the end of this week, I committed your suggestion. Thanks for that :) |
cffeda8
to
768950c
Compare
@tsmaeder I have rebased the PR and adapted to the latest changes (msgpack).
The recent change to msgpack and other bugfixes seem to have resolved this issue. I can no longer reproduce it @paul-marechal This PR now also includes your fix to ensure that the BinaryMessagePipe is working when using Windows. |
const onCloseEmitter = new Emitter<ChannelCloseEvent>(); | ||
const onErrorEmitter = new Emitter<unknown>(); | ||
const onMessageEmitter = new Emitter<MessageProvider>(); | ||
|
||
// Create RPC protocol before adding the listener to the watcher to receive the watcher's cached messages after the rpc protocol was created. | ||
const rpc = new RPCProtocolImpl({ | ||
close: () => { | ||
onCloseEmitter.dispose(); | ||
onErrorEmitter.dispose(); | ||
onMessageEmitter.dispose(); | ||
}, | ||
getWriteBuffer: () => { | ||
const writer = new Uint8ArrayWriteBuffer(); | ||
writer.onCommit(buffer => { | ||
this.server.onMessage(pluginHostId, buffer); | ||
}); | ||
return writer; | ||
}, | ||
onClose: onCloseEmitter.event, | ||
onError: onErrorEmitter.event, | ||
onMessage: onMessageEmitter.event | ||
}); |
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 could deserve a dedicated function. What do you think?
The same logic is also in packages/plugin-ext/src/hosted/browser/plugin-worker.ts
and packages/plugin-ext/src/hosted/browser/worker/worker-main.ts
.
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.
Introduced a new "BasicChannel" class with 2b4016e to reduce the need for code duplication.
this.worker.onerror = e => onErrorEmitter.fire(e); | ||
|
||
this.worker.onerror = e => console.error(e); |
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.
One of them is probably not needed.
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.
Fixed with 2b4016e
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.
Mostly questions.
In testing I get the following error message in the log (with electron)
|
@tortmayr I didn't see anything about the comments in #11261 (comment). What's the story there? Otherwise, the code looks fine to me now. |
Ah sorry, I forgot to add a comment regarding this. We need msgpackR extension for error encoding to keep the original stacktrace information (i.e. the pendant to
I'm going to add this once the discussion regarding the msgpackR extension mechanism is resolved (#11261 (comment)) |
63a9a11
to
25c7d1f
Compare
@tsmaeder I pushed an update that improves the error encoding via msgpack and preserves the stack trace. e.g. trying to execute a non-existing command in a plugin would previously result in this message: 2022-11-29T08:18:18.440Z root ERROR [hosted-plugin: 449972] Promise rejection not handled in one second: Error: Command with id 'non.existing.command' is not registered. , reason: Error: Command with id 'non.existing.command' is not registered.
2022-11-29T08:18:18.444Z root ERROR [hosted-plugin: 449972] With stack trace: Error: Command with id 'non.existing.command' is not registered.
at /home/tobias/Git/OpenSource/theia/theia/node_modules/msgpackr/dist/node.cjs:971:39
at read (/home/tobias/Git/OpenSource/theia/theia/node_modules/msgpackr/dist/node.cjs:411:15)
at Array.readObject [as read] (/home/tobias/Git/OpenSource/theia/theia/node_modules/msgpackr/dist/node.cjs:504:18)
at recordDefinition (/home/tobias/Git/OpenSource/theia/theia/node_modules/msgpackr/dist/node.cjs:964:19)
at read (/home/tobias/Git/OpenSource/theia/theia/node_modules/msgpackr/dist/node.cjs:400:13)
at checkedRead (/home/tobias/Git/OpenSource/theia/theia/node_modules/msgpackr/dist/node.cjs:195:13)
at Packr.unpack (/home/tobias/Git/OpenSource/theia/theia/node_modules/msgpackr/dist/node.cjs:105:12)
at Packr.decode (/home/tobias/Git/OpenSource/theia/theia/node_modules/msgpackr/dist/node.cjs:177:15)
at MsgPackMessageDecoder.decode (/home/tobias/Git/OpenSource/theia/theia/packages/core/lib/common/message-rpc/rpc-message-encoder.js:84:29)
at MsgPackMessageDecoder.parse (/home/tobias/Git/OpenSource/theia/theia/packages/core/lib/common/message-rpc/rpc-message-encoder.js:87:21) and now correctly preserves the stacktrace of the original error: 2022-11-29T08:23:58.196Z root ERROR [hosted-plugin: 455858] Promise rejection not handled in one second: Error: Command with id 'non.existing.command' is not registered. , reason: Error: Command with id 'non.existing.command' is not registered.
2022-11-29T08:23:58.197Z root ERROR [hosted-plugin: 455858] With stack trace: Error: Command with id 'non.existing.command' is not registered.
at CommandRegistryMainImpl.$executeCommand (http://localhost:3000/packages_plugin-ext_lib_hosted_browser_hosted-plugin_js.js:1436:19)
at http://localhost:3000/packages_plugin-ext_lib_common_plugin-api-rpc_js.js:955:71
at async RpcProtocol.handleRequest (http://localhost:3000/bundle.js:142581:28) |
@tortmayr just a hint: I consider it good practice to keep updates to PR's in separate commits until ready to merge: it makes it possible just review the delta. |
Noted, thanks. Apparently I have interacted with too many gerrit based systems recently and developed the bad habit of rebasing/force pushing all changes into a single commit 😉 |
It's a bit early for a flamewar ;-) |
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.
Some more testing did not reveals anything untoward for me. I'm fine with the code as it stands now.
@paul-marechal it looks to me your like your comments have been addressed and we're ready to merge this one? |
For anyone interested I did some benchmarking for plugins and frontend plugins to compare the file reading performance starting from the 1.26 Release: BenchmarksTest scenario: Read a file of size x from the workspace using the FileSystem plugin API.
Browser
Electron
VS Code
|
@tsmaeder @tortmayr I had trouble formulating what was bothering me but I think I got it: I don't understand the whole rationale behind the msgpackr extension API:
Obligatory "it works right now" so if you are in a hurry and want to merge feel free to do so, but this is my only outstanding concern. |
Lastly I also am not a fan of the class + static member way of creating JS singletons as it's more of a Java way. Why not just exposing a global function like msgpackr does? |
Totally agreed. This global extension mechanism is less than optimal. However, it's currently the only way to customize the encoding of certain types. We can try and raise this issue in the msgpackR repo. From what I could see up until now maintainers are quite open for feedback and suggestions so we might be able to get a fix for this in the upstream repo.
The I guess this dependency on ResponseErrors is a remainder from the original vscode-jsonrpc protocol and we should probably refactor the code to remove it. IMO this is also something that can easily be handled in a follow-up task. Nevertheless, I will take a look on it. If it can be removed rather easily without any side effects I will do it right away.
They do require a custom extension. We currently register custom extension at two places: In a nutshell, we have three topics that require custom encoding/decoding which can currently only be done (efficiently) with msgpackr extensions:
I agree, a global function is probably the lesser evil here. |
IMO, we're ready to merge this one:
|
I would be in favor of merging this asap. This will give us the chance to fix potential bugs before the next release. |
Refactors the plugin RPC protocol to make use of the new message-rpc introduced with eclipse-theia#11011/eclipse-theia#11228. - Refactor plugin-ext RpcProtocol API to reuse the new message-rpc protocol - Remove custom RPC message encoding and handling reuse message-rpc - Implement `BatchingChannel` that queues messages and sends them accumulated on the next process.tick (replaces the old Multiplexer implementation) - Refactors proxy handlers and remote target handlers - Use `Channel` instead of `MessageConnection` for creating new instances of RPCProtocol - Refactor `RpcMessageEncoder`/`RpcMessageDecoder` to enable overwritting of already registered value encoders/decoders. - Add mode property to base `RpcProtocol` to enable switching from a bidirectional RPC protocol to a client-only or server-only variant. - Implement special message encoders and decoders for the plugin communication. (Replacement for the old `ObjectTransferrer` JSON replacers/revivers) - Adapt `HostedPluginServer` and `HostedPluginClient` API to send/receive messages in binary format instead of strings. This enables direct writethrough of the binary messages received from the hosted plugin process. - Adapt `hosted-plugin-process` and `plugin-host` to directly send binary messages via `IpcChannel`/`BinaryMessagePipe` - Remove incorrect (and unused) notification proxy identifiers and instantiation - NotificationExt was instantiated in the main context - There were unused notification proxy identifiers for main and ext in the wrong contexts Part of eclipse-theia#10684 Fixes eclipse-theia#9514 Contributed on behalf of STMicroelectronics
7927546
to
2711928
Compare
@tsmaeder @paul-marechal @JonasHelming |
What it does
Refactors the plugin RPC protocol to make use of the new message-rpc introduced with #11011/#11228.
Refactor plugin-ext RpcProtocol API to reuse the new message-rpc protocol
QueuingChannelMultiplexer
that queues messages and sends them accumulated on the next process.tick (replaces the old Multiplexerimplementation)
Channel
instead ofMessageConnection
for creating new instances of RPCProtocolRpcMessageEncoder
/RpcMessageDecoder
to enable overwritting of already registered value encoders/decoders.RpcProtocol
to enable switching from a bidirectional RPC protocol to a client-only or server-only variant.Implement special message encoders and decoders for the plugin communication. (Replacement for the old
ObjectTransferrer
JSON replacers/revivers)Adapt
HostedPluginServer
andHostedPluginClient
API to send/receive messages in binary format instead of strings. This enables direct writethrough of the binary messages received from the hosted plugin process.Adapt
hosted-plugin-process
andplugin-host
to directly send binary messages viaIpcChannel
/BinaryMessagePipe
Remove incorrect (and unused) notification proxy identifiers and instantiation
Part of #10684
Fixes #9514
Co-authored-by: Lucas Koehler [email protected]
Contributed on behalf of STMicroelectronics
How to test
For regression testing, any plugin loaded in the example applications can be used. Everything (i.e. interacting with and using the plugin should work as before.
For performance testing see comment below
Review checklist
Reminder for reviewers