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

Run ydoc-server with GraalVM #9528

Merged
merged 61 commits into from
May 2, 2024
Merged

Run ydoc-server with GraalVM #9528

merged 61 commits into from
May 2, 2024

Conversation

4e6
Copy link
Contributor

@4e6 4e6 commented Mar 25, 2024

Pull Request Description

part of #7954

Important Notes

The workflow is:

  • $ npm install -- just in case
  • $ npm --workspace=enso-gui2 run build-ydoc-server-polyglot -- build the ydocServer.js bundle
  • $ sbt ydoc-server/assembly -- build the ydoc server jar
  • env POLYGLOT_YDOC_SERVER=true npm --workspace=enso-gui2 run dev -- run the dev server with the polyglot ydoc server. Providing POLYGLOT_YDOC_SERVER_DEBUG=true env variable enables the chrome debugger

Checklist

Please ensure that the following checklist has been satisfied before submitting the PR:

  • The documentation has been updated, if necessary.
  • Screenshots/screencasts have been attached, if there are any visual changes. For interactive or animated visual changes, a screencast is preferred.
  • All code follows the
    Scala,
    Java,
    and
    Rust
    style guides. In case you are using a language not listed above, follow the Rust style guide.
  • All code has been tested:
    • Unit tests have been written where possible.
    • If GUI codebase was changed, the GUI was tested when built using ./run ide build.

@4e6 4e6 added the CI: No changelog needed Do not require a changelog entry for this PR. label Mar 25, 2024
@4e6 4e6 self-assigned this Mar 25, 2024
}

public static void main(String[] args) throws Exception {
ClasspathResource.createTempFile(WASM_PATH);
Copy link
Member

Choose a reason for hiding this comment

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

According to @kazcw we should just call the Parser.parseInput instead of trying to load Rust part of the parser as WASM.

Copy link
Contributor

@kazcw kazcw Mar 25, 2024

Choose a reason for hiding this comment

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

We can, and it would be more efficient--but doing so would have implications for versioning, a topic which I think in the 13-March meeting we decided to decide later. One possibility discussed was the UI injecting the parser (which would be the WASM core and the JS layer) so that the frontend could remain tightly coupled with it, and then we would use something else as the stable interface (I pictured an IrBuilder API).

If we are definitely OK with requiring the frontend and backend to be versions with the same parser build, we should make the change to using the linked library (and we should add a parser version check to the protocol).

Copy link
Member

@JaroslavTulach JaroslavTulach Mar 25, 2024

Choose a reason for hiding this comment

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

Right now versioning isn't an issue (everything is packaged as a single deliverable, distribution). But should IDE inject its version of the yserver into (completely) different version of an engine, then relying on engine's parser isn't an option. I should collect these constraints somewhere.

Copy link
Contributor

Choose a reason for hiding this comment

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

Right now versioning isn't an issue (everything is packaged as a single deliverable, distribution).

Versioning isn't an issue in production, but the only reason versioning doesn't currently seem like an issue during development is that the frontend and backend are loosely-coupled in practice, with our current source-code/span-tree interface. When we move the parser into the ydoc-server, it will become essential that the frontend and backend are using the same parser version (including both the WASM and app/gui2/shared).

Copy link
Member

@JaroslavTulach JaroslavTulach Mar 26, 2024

Choose a reason for hiding this comment

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

Versioning isn't an issue in production

..in current production we provide two kinds of deliverables:

With such a setup we may pay no attention to versioning...

frontend and backend are loosely-coupled in practice

... as frontend and backend are build time coupled - e.g. they are build, tested and known to work together. In such a setup it is clear that ...

the frontend and backend are using the same parser version

They do use the same version, as they are built, tested, packaged together.

As such I am suggesting to @4e6 to call the JNI version of the parser. I believe it is a simpler, few lines code change. Running our parser in its WASM version is also possible (there is GraalWasm, but it is more complex to setup and it'd be (slightly) slower, slower to get up to the full speed and more memory hungry because of WASM interpreter metadata overhead. That's why I'd start with integrating the parser via its JNI version. Only then investigate how to integrate the WASM variant.

Please find a link to additional information about incremental compilation plan here.

Copy link
Contributor

@kazcw kazcw Mar 26, 2024

Choose a reason for hiding this comment

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

Versioning isn't an issue in production

..in current production we provide two kinds of deliverables:

* [ standalone packages containing both GUI and the backend](https://github.com/enso-org/enso/releases/tag/2024.1.1-nightly.2024.3.26)

* the cloud which also packages GUI and backend of the _same release tag_ like [2024.1.1-nightly.2024.3.26](https://github.com/enso-org/enso/releases/tag/2024.1.1-nightly.2024.3.26) (right @PabloBuchu?)

With such a setup we may pay no attention to versioning...

frontend and backend are loosely-coupled in practice

... as frontend and backend are build time coupled - e.g. they are build, tested and known to work together. In such a setup it is clear that ...

the frontend and backend are using the same parser version

They do use the same version, as they are built, tested, packaged together.

Right... "versioning isn't an issue in production."

However, this change would cause some inconvenience during development. Developers frequently use versions other than the packaged versions enumerated above. It is currently possible for frontend developers to pull changes from develop without rebuilding the backend. I do so frequently, and I expect I am not alone in this. If we make this change, the coupling will be much tighter in practice. Frontend developers (and library developers?) will need to be aware when a commit touches the parser and they need to rebuild the backend. Developers working on the parser will have a much longer code/test cycle. Maybe this is acceptable, but these are costs we should be aware of if we're making this change.

var data = arguments[2].as(int[].class);

var charset = encoding == null ? StandardCharsets.UTF_8 : Charset.forName(encoding);
// Convert unsigned Uint8Array to byte[]
Copy link
Member

@JaroslavTulach JaroslavTulach Apr 3, 2024

Choose a reason for hiding this comment

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

Try to obtain .as(ByteBuffer.class) - I've heard it should be possible to exchange it from/to Uint8Array.

Copy link
Contributor Author

@4e6 4e6 Apr 9, 2024

Choose a reason for hiding this comment

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

The ByteBuffer interop currently works only with arrays that are backed by the Java buffers https://github.com/oracle/graaljs/blob/master/graal-js/src/com.oracle.truffle.js.test/src/com/oracle/truffle/js/test/interop/InteropByteBufferTest.java

We may try using Value.readBuffer method to extract byte array without the conversion

@4e6 4e6 force-pushed the wip/db/graalvm-ydoc-server branch from d71e200 to f1da6ec Compare April 11, 2024 10:17
@4e6 4e6 force-pushed the wip/db/graalvm-ydoc-server branch 4 times, most recently from da0280b to 9d7c750 Compare April 17, 2024 20:22
/// Determine the token variant of the provided input.
#[allow(unsafe_code)]
#[no_mangle]
pub extern "system" fn Java_org_enso_syntax2_Parser_isIdentOrOperator(
Copy link
Member

Choose a reason for hiding this comment

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

This method is also needed here: #8779 (comment)

  • from code clarity perspective - we should have just a single implementation
  • from performance perspective - it is better to stay in the JVM than jump out and back just to compare letters in a string - the overhead of input buffer conversion isn't negliable
    • not only a JVM vs. JNI problem
    • also JavaScript vs. WASM problem

However if the performance wasn't a problem so far, we probably don't need to care. It is more important to get the functionality right.

@4e6
Copy link
Contributor Author

4e6 commented Apr 19, 2024

It's alive! First run with the polyglot ydoc server.

ydoc_server_first.mp4

@JaroslavTulach
Copy link
Member

JaroslavTulach commented Apr 20, 2024

It's alive! First run with the polyglot ydoc server.

I can reproduce your success myself if I:

enso$ POLYGLOT_YDOC_SERVER=true npm --workspace=enso-gui2 run build-ydoc-server-polyglot
enso$ sbt ydoc-server/assembly
enso$ POLYGLOT_YDOC_SERVER=true npm --workspace=enso-gui2 run dev

Small step for ydoc, but a huge leap for Enso! Congrats.

@JaroslavTulach
Copy link
Member

Small step for ydoc, but a huge leap for Enso! Congrats.

I even got debugging working with:

enso$ POLYGLOT_YDOC_SERVER_DEBUG=true npm --workspace=enso-gui2 run dev
ydoc: Debugger listening on ws://127.0.0.1:34567/HwpxX-S2_c-dB0qRAMhY-LmSWALU7RSvXF5IBRkgeoA
For help, see: https://www.graalvm.org/tools/chrome-debugger
ydoc: E.g. in Chrome open: devtools://devtools/bundled/js_app.html?ws=127.0.0.1:34567/HwpxX-S2_c-dB0qRAMhY-LmSWALU7RSvXF5IBRkgeoA

I can connect to your process via ChromeDevTools:

obrazek

@4e6 4e6 marked this pull request as ready for review May 1, 2024 11:42
Copy link
Contributor

@somebody1234 somebody1234 left a comment

Choose a reason for hiding this comment

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

approving dashboard changes
(the only changes there is a new ydocUrl parameter that gets threaded through the entire app to Editor.tsx)

@hubertp hubertp added the CI: Clean build required CI runners will be cleaned before and after this PR is built. label May 1, 2024
Copy link
Member

@JaroslavTulach JaroslavTulach left a comment

Choose a reason for hiding this comment

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

  1. I see the whole support is optional right now - it doesn't affect workflow yet. Let's integrate and move on!
  2. Please make sure the instructions in PR description to build and use this in the description are correct
  3. Having a CI test that performs these instructions and runs (and shutdowns automatically) the ydoc-server would be a plus, but it is not strictly necessary right now.

@@ -14,6 +14,8 @@ public class Main {
private Main() {}

public static void main(String[] args) throws Exception {
System.setProperty("helidon.serialFilter.pattern", "javax.management.**;java.lang.**;java.rmi.**;javax.security.auth.Subject;!*");
Copy link
Member

@JaroslavTulach JaroslavTulach May 2, 2024

Choose a reason for hiding this comment

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

I found this advice on stackoverflow and it does seem to help and configure the JEP-290 filter, so it allows JMX. I can connect to the process with JVisualVM polyglot sampler now.

polyglot sampler

I'd say the first edit is fine on my computer.

@4e6 4e6 added the CI: Ready to merge This PR is eligible for automatic merge label May 2, 2024
@mergify mergify bot merged commit 5995a00 into develop May 2, 2024
37 checks passed
@mergify mergify bot deleted the wip/db/graalvm-ydoc-server branch May 2, 2024 06:28
@hubertp
Copy link
Contributor

hubertp commented May 2, 2024

It does feel like the first edit is taking considerably longer.

Copy link
Contributor

@hubertp hubertp left a comment

Choose a reason for hiding this comment

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

post merge comments

}
if (!existsSync(ydocServerJar)) {
const cwd = fileURLToPath(new URL('../..', import.meta.url))
const sbt = spawn('sbt', ['ydoc-server/assembly'], { cwd })
Copy link
Contributor

Choose a reason for hiding this comment

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

That seems like an unnecessary dependency. If someone forgot to build the jar, tough luck.

Comment on lines -691 to +692
frgaalJavaCompilerSetting
Compile / javaSource := baseDirectory.value / "generate-java" / "java"
Copy link
Contributor

Choose a reason for hiding this comment

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

Why no longer under frgaal?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It caused some compilation issues. Disabled because the project does not use Java 21 features.

build.sbt Show resolved Hide resolved
var ydoc = Main.class.getResource(YDOC_SERVER_PATH);
var contextBuilder = WebEnvironment.createContext().allowIO(IOAccess.ALL);

Sampling.init();
Copy link
Contributor

Choose a reason for hiding this comment

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

optional?

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, optional. Just moved the logic to a separate class.

log.debug(Arguments.toString(arguments));

return switch (command) {
case RANDOM_UUID -> UUID.randomUUID().toString();
Copy link
Contributor

@hubertp hubertp May 2, 2024

Choose a reason for hiding this comment

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

isn't this a c&p mistake? There is no random-uuid for AbortController

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll clean it up in the next PR

hubertp added a commit that referenced this pull request May 2, 2024
Addressing some PR comments, DRY.
hubertp added a commit that referenced this pull request May 2, 2024
Addressing some PR comments, DRY.
@kazcw kazcw mentioned this pull request May 2, 2024
2 tasks
mergify bot pushed a commit that referenced this pull request May 7, 2024
While playing with the implementation addressed some PR comments of mine and applied DRY.
@JaroslavTulach
Copy link
Member

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CI: Clean build required CI runners will be cleaned before and after this PR is built. CI: No changelog needed Do not require a changelog entry for this PR. CI: Ready to merge This PR is eligible for automatic merge
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants