6 ms·
Fast Vue SSR with Rust and QuickJS
- alttab 6y agoUsing JS to do SSR? How the turntables.
- tyingq 6y agoIt wasn't well liked, but Netscape Livewire did JS SSR in the 90's.
- gutino 6y agoIf memory is the concern, node V8 Engine has the mode "lite" mode: https://v8.dev/blog/v8-lite https://v8.dev/blog/v8-lite with that perhaps you could spawn node to all threads and do even better in terms performance/memory?
- xucheng 6y agoIt seems that there is a JS context per worker and the workers are shared among different requests. Further, the requests are handled by simple string formatting and eval in the JS context. This means that it should be trivial to inject arbitrary js codes in the server and initiate a XSS attack.
- jgalvez 6y agoEach thread runs the renderer sequentially, which means we can share a VueRouter instance between them as well. At least so far haven't seen any issues with it. This is highly experimental, just a proof-of-concept so far. Security issues need to be reviewed and addressed for sure.
- spankalee 6y agoClient side code can assume a single-user context. It could write user-specific values to globals, use them in modules, etc., and it would be fine on the client side. But run in a multi-user environment it would be relatively easy to leak data from one user to another. SSR should run each request, or at least session, in a fresh context.
- jgalvez 6y agoThat would be the case in a typical Node setting, but in this case, each thread runs the renderer no more than once at a time. So we just have to provide a fresh state.
- dragonsh 6y agoIt’s been a observation for quite sometime add Rust to any heading and most likely will end up on top of HN. Didn’t expect such a trivial thing to be in front of HN, but probably Rust marketing and evangelist are all on HN waiting to move them to top. Stil waiting for good library in rust not dependent on C or unsafe code. Looking at most of the cargo libraries most good one’s are a wrapper around C or C++ library or use unsafe code.
- Avi-D-coder 6y agoThere will always be unsafe and often be C dependencies. There is no community goal or plan to do away with unsafe. Until we have an easy to use fully dependent and linearly typed language, there will be some degree of unsafety. Even if Idris 3 and ATS 4 come out with magical proof inference, we will assert many more complicated proofs.
- dragonsh 6y agoRust community has been very vocal placing Rust as C replacement and based on your statement it is not. So it’s better for systems programmer to first focus on C/C++ to be closer to hardware and when needs some higher level work look at Rust for writing higher level libraries not dealing directly with hardware.
- dbaupp 6y agoThe grandparent didn’t say anything about why C dependencies will often exist: in almost all cases, it is not because they must exist due to missing functionality in Rust, but because it is easier to use a C library than rewrite/translate into Rust. Once someone has done that process (or implemented something equivalent), using a pure Rust dependency is nicer for many reasons, but reimplementing a complicated library is always hard, no matter the language used.
- dragonsh 6y ago> it is not because they must exist due to missing functionality in Rust This is the precise reason, Rust is a decade or two away from being a useful replacement for C/C++ like other contending systems programming language like go and Swift which are at present more popular than Rust for writing systems program for networking, servers and controlling Apple hardware directly.
- kbumsik 6y agoSo, how is it "Fast", without any performance benchmarks? Are we just assuming that it is fast just because it is written in Rust?
- gandutraveler 6y agoServer side rendering ? But yeah benchmarks would be great to compare
- jgalvez 6y agoHaven't had time to do proper benchmarks yet, but it does perform way faster than a single Node process serving the same Vue.js app via Nuxt.js, using fewer memory. This is likely to vary for large, complex apps for sure. And yes, Node will beat QuickJS in benchmarks by a wide margin, but then again, I don't think you can run 64 tiny threads with V8 like I do with this thing :)
- vijaybritto 6y agoNice effort. I would like to see SSR for React run in the same way!
- jgalvez 6y agoOn my computer, single process Node vs single process Rust: - Rust: 20k requests in 10.04s - Node: 4k requests in 10.11s Tested with `autocannon http://localhost:3030` http://localhost:3030`. Running multiple Node processes will beat it for sure. But then you're using a lot more memory and CPU, I think.
- sradman 6y agoI understand that Rust Hyper and/or Warp provide a very fast httpd engine but I would have thought that invoking JavaScript via Deno/V8, instead of QuickJS, was the natural integration. Maybe Deno is hard to invoke from Rust Code.
- chrismorgan 6y agoThe Deno core is easy to embed in Rust (or so they say; I haven’t tried it). https://deno.land/manual/embedding_deno https://deno.land/manual/embedding_deno. Note that this is the core and doesn’t include TypeScript support. Also note that the rusty_v8 bindings will by default download a prebuilt version of V8 from GitHub, because otherwise it can be a mild pain to set up and takes a long time to build. See https://github.com/denoland/rusty_v8 https://github.com/denoland/rusty_v8. I can easily see why one would go with QuickJS: it’ll be much quicker to get going.
- the_duke 6y agoQuickJS is a simple interpreter, no JIT, so performance obviously won't be competitive with V8. The appeal is that it supports most of ES2019 while being a easy to build, light weight dependency that can be embedded into Rust applications. [1] Would I use something like this for a production service with a high traffic volume? It would be nice to see benchmarks, but most likely not. But if you have a Rust backend and want a simple way to do SSR without having to operate a separate Node service or linking v8, this might be a neat approach. ps: It would be nice to run QuickJS in a WASM runtime, while still offering the same convenient API surface for Rust. This would provide sandboxing for the QuickJS C code. It's is in my backlog, I might get to it eventually. [2] [1] https://github.com/theduke/quickjs-rs https://github.com/theduke/quickjs-rs [2] https://github.com/theduke/quickjs-rs/issues/11 https://github.com/theduke/quickjs-rs/issues/11
- jgalvez 6y agoExactly where my thoughts are with this.
- JMTQp8lwXL 6y agoSlightly off topic, but is ES<CalendarYear> an actual thing, or common parlance for current capabilities? Officially, I thought there was only ES5, ES6, etc.
- Cu3PO42 6y agoYes, the standards have been named like that since 2015. In fact ES6 was renamed to ES2015. Since then there have been yearly "releases"/standard versions.
- amelius 6y agoPersonally I think it's a huge waste to re-write everything (badly) in Rust. Why not link against an existing JS compiler/interpreter written in another language?
- wmf 6y agoQuickJS is an existing JS compiler/interpreter written in C.
- mhd 6y agoI'll definitely use this once Fabrice Bellard gets around to writing TinyRust.
- maxfrai 6y agoI think you should post about this project to reddit: https://www.reddit.com/r/rust https://www.reddit.com/r/rust
- andybak 6y agoI hate post titles containing TLAs I don't recognise. (Three letter acronyms before anyone accuses me of hypocrisy)
- etbusch 6y agoSSR - Server Side Rendering
- Matthias247 6y agoBe aware about a couple of issues in the implementation: - The Warp handlers are blocked by the use of thread-blocking synchronization primitives (locks, channels) - which might harm performance and can even lead to deadlocks. - The architecture with "passing work off to a threadpool via a chnanel and waiting for it to complete" is not very resilient, and prone to behave badly under high load. The reason is basically the inifinite queuing - if new requests come in faster than previous ones can be processed the queue length (and thereby the memory footprint of the app) will grow and grow. At some point the application will just be busy serving old queued up requests, which might already have timed out on the client side and potentially even being retried. That's a vicious circle, and you can only get out ot it by shutting down the app. One way to fix this is to limit the queue/channel size and perform load-shedding on requests which can not be enqueued.
- jgalvez 6y agoThanks. Indeed, this is pretty much my first successful attempt. I'm looking into using an async thread pool next.
- jgalvez 6y agoStill it could be used reliably with the help of a load balancer and health checks. I can see Kubernetes spawning pods running this server a lot quicker than it does for Node ones.