-
Notifications
You must be signed in to change notification settings - Fork 226
Allow compiling to assembly and friends in the orchestrated Docker container #914
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
Conversation
Before I even get into the code, I'll respond to your overview — this means I might misunderstand something that is explained by the code I haven't read yet.
Seems reasonable. Will this immediately start the Docker container? If so, it wouldn't surprise me if we need to decouple that in the future. I expect (but have not tested) that starting Docker even if we do nothing will be a noticeable resource usage. There are likely more people just idling on the playground page without needing an active container. In addition to lazily starting the Docker container, I could see a world where we have a pool of idle Docker containers that a WebSocket connection grabs from when it is first needed — that way the initial response can be even quicker. In that case, the pool of coordinators may not be needed; I'm unclear on what resource the pools are attempting to conserve. In the future, we may also need to support a cap on concurrent workers to be able to throttle.
Fits with my expectations.
"string requests" sounds odd. My expectation is that we will send structured JSON requests (e.g.
Lowering to smaller operations makes sense. My hope is that the worker doesn't know anything about
Hopefully this is flexible. Certain things will want to wait and others will not. For example, it makes sense to stream the compiler's warnings before we show the process output (which may take several seconds to compute). However, for our backwards compatibility purposes (e.g. the existing HTTP endpoints), we will definitely need to aggregate everything.
I don't think there should be any recycling. If I run a program that writes to disk, then there should not be a way for you to read that file later. Recall that the current implementation of the playground creates a brand new Docker container for every request, so it's not terribly expensive.
✅
I think that it makes sense to have classes of requests. For example, performing two MIR requests back-to-back is useless and cancelling makes sense. In the future, we could even be smarter and detect if the code has changed and skip the second request (although this becomes tricky with non-pure programs). However, it does make sense to be able to view the MIR and ASM concurrently, or run the program and look at the LLVM IR.
Sounds like this can be addressed by trashing the entire container when the server is done with it, as mentioned above.
I do the exact same thing when developing locally — building hundreds of crates is not fun for a quick cycle time!
I'm not really following this point. Do note that some of my separate work changes how the JS <-> backend messages are formed, so we'll likely need to synchronize anyway.
Yep, I've got some rough UI for that. |
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.
Ok, I read through the production code, but haven't started on the test code yet. My browser is acting up a bit with the comments in the queue, and it's lunch time, so I'll submit for now and try to circle back soon.
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 higher-level thoughts; I didn't look too deeply at the exact code implementation of the tests.
ui/src/coordinator.rs
Outdated
match server_msg { | ||
None => { | ||
panic!("Server disconnected"); | ||
} |
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 is Option::expect
We do have a pool of idle Docker containers and coordinator is unique globally.
It works in this case.
Good use cases. We can queue requests to process one by one since there might be conflicts when two compilation command works in the same project directory at the same time. |
91d3507
to
57b8e74
Compare
orchestrator/src/coordinator.rs
Outdated
#[derive(Debug)] | ||
pub enum PlaygroundMessage { | ||
Request(RequestId, HighLevelRequest), | ||
StdinPacket(CommandId, String), |
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.
I don't think that we need stdin support at all for compilation, so that should probably be removed from this PR and saved until later.
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.
I decided against actually removing this. It's unused and untested, but probably won't be too much trouble.
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.
Re-skimming; haven't made it through sandbox
/ worker
/ message
yet.
orchestrator/src/main.rs
Outdated
#[tokio::main(flavor = "current_thread")] | ||
pub async fn main() { | ||
#[snafu::report] |
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.
We aren't actually looking at the stderr
on the coordinator side yet. It's being streamed to the coordinator's stderr, but that won't be useful in a web context.
orchestrator/src/coordinator.rs
Outdated
) -> Result<ActiveCompilation, CompileError> { | ||
use compile_error::*; | ||
|
||
let output_path: &str = "compilation"; |
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.
The current playground has to do some ugly tricks to find the right file; I wonder if we can avoid that now.
8405d65
to
f2c545c
Compare
All right... I finally had a chance to get the last bit of this first step to what I think is a mergable and deployable state. I'm going to let CI run on this a little and maybe let it sit for a few days to see if any reviews come in. |
f2c545c
to
778c7fc
Compare
778c7fc
to
2dabd5f
Compare
d7c2c1f
to
44fb680
Compare
44fb680
to
0c12e13
Compare
0c12e13
to
9ec333b
Compare
9ec333b
to
95a9c34
Compare
95a9c34
to
4670840
Compare
The orchestrator allows starting up a Docker container and then communicating with it while it's running. We can then request files to be read or written in the container as well as running processes. Just enough has been done to connect the pieces needed to support compiling to assembly / LLVM / MIR, etc. Future work will add other operations including the ability to run the code. Co-authored-by: Jake Goulding <[email protected]>
4670840
to
8b8a3dd
Compare
Since this PR has changes to the CI (which I disallow for security reasons), I pushed a trusted branch and it passed CI. \ 🚀 |
cc @shepmaster .
Finally make it usable at least for one use case - Show MIR.
The coordinator works as following:
COORDINATOR
which allocates(spawn one if none is free) aContainer
as a WebSocket connection comes in.Container
consists of two tasks de/encode messages from/to its underlyingworker
process stdio, and an event loop task which is central of functionality.worker
.worker
, it aggregates them into high-level response likeCompileResponse
and send stringified response to WebSocket.Container
only exposes two channels which are used for bridging WebSocket and event loop.COORDINATOR
after WebSocket disconnects.Some notes:
websocket
.worker
doesn't clean temporary files after recycling currently. Another TODO. Easy to fix.stable
channel is created and it contains no external crates.run
code. Another TODO.