Compilify is an online compiler as a service, started by Justin Rusbatch, which works on top of the Roslyn CTP. Started recently, it has already received a good amount of attention from the .NET community. We got in touch with Justin to ask him some questions.
InfoQ: Could you introduce yourself to our readers?
Justin: My name is Justin. I’m a self-taught developer working for a small .NET-based web development company in central Pennsylvania. I taught myself C# while working night-shifts as a Computer Operator, in between mounting tapes for a mainframe. I worked with ASP.NET webforms for a year, but I moved on to the MVC framework when it came out and I’ve been developing sites with that ever since. I enjoy learning other languages. I have some experience with Ruby on Rails, node.js, and F#.
InfoQ: You recently started Compilify - could you explain what is the purpose of this project?
Justin: Compilify (pronounced “compile-ify”) was inspired by many things. One of the primary sources of inspiration was the C# Interactive window that the Roslyn CTP brings to Visual Studio. This window provides a REPL environment that can be used to execute individual statements against the projects they’re developing and receive immediate results.
Compilify makes the .NET compiler completely portable and accessible through a fast, simple interface that fosters sharing and collaboration. It’s not an IDE in your browser, nor will it ever be. It’s simpler than that. You shouldn’t need to launch an IDE or create a new console project to experiment with a new idea that only takes a few lines of code. A developer’s time is incredibly valuable. Spending too much time thinking about a problem without actually trying solutions results in over-engineered solutions, killing productivity.
It also has a lot of potential as an educational tool for people who don’t know C# but want to try it. Downloading, installing, and launching Visual Studio can intimidate new developers. In fact, some developers might not install Visual Studio because of all the other applications that it install with it -- leading to an even more overwhelming experience. Compilify lets users get their hands dirty without needing to install anything -- not even a browser plugin.
InfoQ: Could you explain how Compilify works under the covers?
Justin: Lots of magic and coffee!
When the user submits code to the server for execution, a persistent connection is opened with SignalR. The web server wraps the code in an object with the SignalR connection ID that it was received from and then added to a processing queue on my Redis server. This frees up the web server to continue handling requests from other users.
Background workers do the heavy lifting. In order to prevent any malicious code from running, a new low-trust AppDomain is created to act as a secure sandbox for each execution. I haven’t taken the time to profile the performance, but so far I haven’t had a reason to worry about performance in this part of the application. Only essential assemblies are loaded into the AppDomain in addition to the user’s code.
The user’s code is wrapped in a method, parsed into a compilation unit, and emitted into an assembly. The assembly is loaded inside the sandbox and the method that the user’s code was wrapped in is called. the result is serialized and returned to the worker. I perform this work in a separate thread that I can cancel if too much time is taken (the time limit is currently set at 5 seconds).Once the result has been returned to the worker, it’s published back to Redis through a pub/sub channel along with the SignalR connection ID that originally made the request for the code to be executed. The web server subscribes to this channel on App_Start. SignalR is then used to forward any messages received in through this channel to the appropriate client.
This complex architecture was necessary to facilitate the secure execution of the user’s code, and to ensure the stability of the web servers.
InfoQ: The compiler response is almost instantaneous, as you type, despite it having to do a round trip to the server. How did you achieve that?
Justin: The process to validate the user’s code starts 0.5 seconds after they finish typing. The contents of the editor are POSTed to the server using a standard AJAX request. There, the code is parsed using Roslyn and checked for syntax or reference errors. I stop short of actually emitting the compilation unit to an assembly, though. Any errors are returned to the client and displayed to the user.
InfoQ: Roughly, how much time/effort did it take for you to build this?
Justin: I started working on Compilify a week and a half before I launched it. It’s far from being complete. In fact, what I released this past Wednesday (April 11th) was really only a proof of concept. I hoped it that by releasing it I’d receive some feedback and possibly stir up some excitement -- but I wasn’t expecting nearly as much traffic as I received.
InfoQ: On average, what kind of traffic are you receiving and how many web servers/workers on average do you need to cater to that traffic?
Justin: In the past week since the site launched on Wednesday, April 11th, it has received almost 20,000 hits. Users have saved, validated, or executed their code over 70,000 times. Most of the traffic occurred after Twitter found the site last week. John Galloway generated a decent load when he tweeted about it -- between 50-60 simultaneous sessions. Then Scott Hanselman tweeted about it and the number tripled, peaking around 170 simultaneous sessions, less than five minutes later. I had to spin up three web servers and two background workers to keep up with the load.
Like I mentioned before, I really wasn’t expecting that kind of traffic, and I probably wouldn’t have been able to handle it if it hadn’t been for help from the guys at AppHarbor. They were easy to get in touch with, and the information they were able to give me helped identify a few changes I could make to reduce the load.
By keeping a Redis queue between the web application (which handles the validation of code), and my background workers (which compile and execute the code) I’m able to easily scale the application. If the queue starts getting backed up, I can spin up more background workers, and if the front-end starts getting overwhelmed, I spin up more web workers. The New Relic add-on for AppHarbor makes it really easy to monitor the load on both my web workers and my background workers.
InfoQ: Any particular learnings you would like to share about working with Roslyn, SignalR, Redis or any other components that you have used in the project?
Justin: SignalR is a powerful tool, and it is incredibly easy to set up, but you need to be mindful about how you use it. It’s also fast, so it feels lightweight. I made the mistake of opening the connection on page load and never closing it. For something like http://jabbr.net, a chat application by David Fowler, this behavior is necessary.
But in my case, it wasn’t necessary. I have no need to push messages to the client until they’ve clicked the link to run their code. And once the results have been pushed to them, there isn’t a need to keep that connection open. The server load dropped dramatically when I started opening the connection only when needed, and closing it once the message was delivered. There are a lot of examples out there for SignalR, but most of them are demonstrating something like Jabbr, so they don’t show you how to close the connection.
Compilify is an open source project, hosted on github. The service is hosted on AppHarbor who recently sponsored the project. They too put up an interview with Justin on their blog which has some more details.