From 5dbe253cb0440fb167f9b5f4215cf1228a78c381 Mon Sep 17 00:00:00 2001 From: Sebastian Jeltsch Date: Tue, 9 Sep 2025 16:25:12 +0200 Subject: [PATCH] Update documentation to cover the new WASM runtime. --- README.md | 2 +- docs/astro.config.ts | 1 + docs/src/components/BarChart.tsx | 4 +- docs/src/components/layout/Hero.astro | 2 +- docs/src/components/layout/SiteTitle.astro | 8 +- .../blog/_switching_to_a_wasm_runtime.tsx | 63 ++++ docs/src/content/blog/_wasm_logo.svg | 8 + .../blog/switching_to_a_wasm_runtime.mdx | 296 ++++++++++++++++++ .../content/docs/comparison/pocketbase.mdx | 20 +- .../content/docs/documentation/apis_js.mdx | 56 ++-- .../docs/getting-started/first-ui-app.mdx | 48 +-- docs/src/content/docs/index.mdx | 10 +- .../reference/_benchmarks/wasm_runtime.tsx | 1 + .../src/content/docs/reference/benchmarks.mdx | 40 ++- docs/src/pages/blog.astro | 2 +- docs/src/pages/blog/[slug].astro | 4 +- 16 files changed, 477 insertions(+), 88 deletions(-) create mode 100644 docs/src/content/blog/_switching_to_a_wasm_runtime.tsx create mode 100644 docs/src/content/blog/_wasm_logo.svg create mode 100644 docs/src/content/blog/switching_to_a_wasm_runtime.mdx create mode 120000 docs/src/content/docs/reference/_benchmarks/wasm_runtime.tsx diff --git a/README.md b/README.md index 49f8ab27..9a07b091 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@

An open, blazingly fast, single-executable Firebase alternative with type-safe REST & realtime APIs, built-in JS/ES6/TS - runtime, SSR, auth and admin UI built on Rust, SQLite & V8. + runtime, SSR, auth and admin UI built on Rust, SQLite & WebAssembly.

diff --git a/docs/astro.config.ts b/docs/astro.config.ts index de6f340b..61dac52b 100644 --- a/docs/astro.config.ts +++ b/docs/astro.config.ts @@ -68,6 +68,7 @@ export default defineConfig({ "http://localhost:4000/**/*", // The link validator fails to validate the OpenAPI pages injected above. `/${openApiBase}/**/*`, + "/blog/**/*", ], }), ], diff --git a/docs/src/components/BarChart.tsx b/docs/src/components/BarChart.tsx index 7ddfdd5c..8e0f11b7 100644 --- a/docs/src/components/BarChart.tsx +++ b/docs/src/components/BarChart.tsx @@ -17,7 +17,9 @@ Chart.register(BarWithErrorBarsController, BarWithErrorBar, ChartDeferred); interface BarChartProps { data: ChartData<"bar">; - scales?: { [key: string]: ScaleOptions<"linear"> }; + scales?: { + [key: string]: ScaleOptions<"linear"> | ScaleOptions<"logarithmic">; + }; } export function BarChart(props: BarChartProps) { diff --git a/docs/src/components/layout/Hero.astro b/docs/src/components/layout/Hero.astro index 76068fdf..d004390c 100644 --- a/docs/src/components/layout/Hero.astro +++ b/docs/src/components/layout/Hero.astro @@ -59,7 +59,7 @@ if (image) {

An open, blazingly fast, single-executable Firebase alternative with type-safe REST & realtime APIs, built-in JS/ES6/TS runtime, SSR, auth - and admin UI built on Rust, SQLite & V8. + and admin UI built on Rust, SQLite & WebAssembly.


diff --git a/docs/src/components/layout/SiteTitle.astro b/docs/src/components/layout/SiteTitle.astro index f6f504bc..250e1c33 100644 --- a/docs/src/components/layout/SiteTitle.astro +++ b/docs/src/components/layout/SiteTitle.astro @@ -139,11 +139,9 @@ const href = pathWithBase(Astro.props.locale || "/"); ), )} - {import.meta.env.DEV && ( - - Blog - - )} + + Blog + ) diff --git a/docs/src/content/blog/_switching_to_a_wasm_runtime.tsx b/docs/src/content/blog/_switching_to_a_wasm_runtime.tsx new file mode 100644 index 00000000..88abb7cd --- /dev/null +++ b/docs/src/content/blog/_switching_to_a_wasm_runtime.tsx @@ -0,0 +1,63 @@ +import type { ChartData } from "chart.js/auto"; +import { BarChart } from "@/components/BarChart.tsx"; + +// const green0 = "#008b6dff"; +const green1 = "#29c299ff"; +const blue = "#47a1cdff"; +const purple0 = "#ba36c8ff"; +const purple1 = "#c865d5ff"; +const purple2 = "#db9be3ff"; +const yellow = "#e6bb1eff"; + +export function RuntimeFib40Times() { + const data: ChartData<"bar"> = { + labels: ["100 runs fib(40) [less is faster]"], + datasets: [ + { + label: "V8", + data: [26.96], + backgroundColor: green1, + }, + { + label: "WASM Rust", + data: [7.14], + backgroundColor: blue, + }, + { + label: "WASM SpiderMonkey JS", + data: [29 * 60 + 43], + backgroundColor: purple0, + }, + { + label: "WASM SpiderMonkey JS + weval", + data: [18 * 60 + 47], + backgroundColor: purple1, + }, + { + label: "WASM custom QuickJS", + data: [11 * 60 + 36], + backgroundColor: purple2, + }, + { + label: "PocketBase (Goja JS)", + data: [16 * 60 + 12], + backgroundColor: yellow, + }, + ], + }; + + return ( + + ); +} diff --git a/docs/src/content/blog/_wasm_logo.svg b/docs/src/content/blog/_wasm_logo.svg new file mode 100644 index 00000000..7e2d2360 --- /dev/null +++ b/docs/src/content/blog/_wasm_logo.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/docs/src/content/blog/switching_to_a_wasm_runtime.mdx b/docs/src/content/blog/switching_to_a_wasm_runtime.mdx new file mode 100644 index 00000000..f6864849 --- /dev/null +++ b/docs/src/content/blog/switching_to_a_wasm_runtime.mdx @@ -0,0 +1,296 @@ +--- +title: > + Switching to a WebAssembly Runtime +pubDate: 2025-09-09 +intro: Going forward TrailBase will rely on a WebAssembly rather than a V8 JavaScript runtime. +tags: ["WASM"] +author: sebastian +image: ./_wasm_logo.svg +--- + +import { RuntimeFib40Times } from "./_switching_to_a_wasm_runtime.tsx"; + +TrailBase has been embedding a V8 JavaScript runtime for the last 10 months +allowing users to implement custom HTTP and job handlers. +During this time we've experienced several issues and limitations - we're +therefore excited to announce that TrailBase is adopting +[wasmtime](https://github.com/bytecodealliance/wasmtime) as a WebAssembly (WASM) +runtime. + +This is - and likely will remain - the biggest user-facing change to TrailBase +🙏. +To ease migration, the plan for releases v0.17 and v0.18 is to be transitional, +i.e. support both runtimes. +First, v0.17 makes the new WASM runtime available allowing us to collect early +feedback and address issues. +We don't expect major changes to the guest APIs. We put a lot of effort into +making the first release usable and all examples have already been migrated. +If everything goes to plan 🤞, v0.18 will then mark V8 for deprecation to +remove it in subsequent releases. + +In the following we're going to touch a bit more about the rational, the +opportunities and what to watch out for. + +## Rational + +Before getting into the benefits of the new runtime, let's quickly touch on some +of the issues we had. + +The V8 JavaScript engine is a amazing piece of engineering. +However, it was never designed as an embeddable or backend-first solution. +It's primary target remains the Chrome browser. +Third-party vendors like Node.js and Deno have taken it upon themselves to +extend V8, with APIs for accessing the file-system, sockets, etc. +These extensions themselves are extensive and have sprawling dependencies. +They're also primarily designed to serve their own ecosystem rather than be +embedded elsewhere. +For reference, ~70% of the `trail` binary are the JavaScript runtime while only +linking a subset of Node.js APIs. +Despite well written, this is a huge chunk of unsafe code with a heightened +level of scrutiny on it due to its pivotal role in browsers. + +In practice, the current JS runtime isn't serving anyone particularly well: + +* it's not Node.js compatible, +* it inflates binary size and the security surface, +* newer deno versions bundle a stale SQLite causing linker issues, and +* prevents us from building "truly" static binaries with MUSL[^1]. + +Comparably, the new WASM runtime is a lot simpler, safer and yet +high-performant. +It also supports [WASI](https://wasi.dev/)[^2], which greatly eases embedding and allows us to +support guests in multiple languages. + +## Opportunities + +Finally, let's talk about some of the immediate and future benefits we can expect... + +### Rigorous State Isolation + +V8's isolates and JIT are expensive, thus they're typically re-used across +requests opening up the gates for accidental state sharing. +There are specialized JIT-free "edge" runtimes like +[LLRT](https://github.com/awslabs/llrt) to specifically solve this issue at the +expense of performance/throughput. +Wasmtime, on the other hand, makes it cheap and easy to spawn fully isolated +instances per request[^4]. +We expect this provide immediate safety benefits for users. + + +### Performance + +This one is a bit more mixed, however combined with an efficient guest environment +(e.g. Rust, C++, ...), wasmtime can outperform V8 by a factor of almost 4. +On the flip side, JS guests will be slower. More on that regression below. + +
+
+ +
+
+ +### Flexible Guest Language Choice + +Many languages support compilation to WASM. This gives users more freedom in +choosing and customizing their server-side environment. +For now, TrailBase supports JS/TS and Rust out-of-the-box. +We have plans to support +[more](https://github.com/bytecodealliance/wasmtime/blob/main/README.md#language-support) +in the future. +Independently and maybe more importantly, WASI makes it straight-forward to +support custom guests. + +Moreover, different endpoints can be implemented in different WASM components +and thus different languages, allowing you to optimize performance as you go. +For example, an expensive, high-QPS endpoint could be rewritten in Rust, which +could yield 10x-100x performance gains. + +### Better I/O Sandboxing + +Previously, isolates were given untethered I/O access, which together with a +very dynamic guest language can pose a security risk. +I/O is now limited to read-only file access for an explicitly provided sandbox +root (`--runtime-root-fs`). +We're planning ot extend I/O capabilities over time on a per-need basis. +If you're missing anything, let us know. + +The new integration also fixes timers - such as `setTimeout` and `setInterval`, which +were unreliable in our previous V8 integration. + +### Less Code + +As mentioned before, switching off V8 removes a lot of high-scrunity code, cuts +binary size roughly in half and lets us build truly static binaries with MUSL. + +### Rethinking Composition & Licensing Model + +The increased flexibility and performance provided by the new WASM runtime +opens up a path to making this a singular entry-point for extending TrailBase +(including SQLite extensions) as opposed to framework use-caess. +In turn, this may allow us to adopt a more popular copyleft license w/o +inflicting obligations on your business-logic. + + +## Regressions + +While WASM can be handily faster than V8 - as seen above - JavaScript in +particular loads and runs significantly slower when compared to a highly +specialized and optimized runtime like V8. +What may feel like a step backward from a JS-centric point of view, may also +provide opportunities to optimize individual endpoints in different languages +based on specific needs. + +In practice, JS is probably one of the least-efficient compile-to-WASM +languages. Instead of emitting immediate WebAssembly, current build flows +bundle an interpreter like SpiderMonkey to work around JS' dynamic nature, i.e. +`eval('/* ... */')`[^3]. + +In practice, using [JCO with SpiderMonkey and weval](https://github.com/bytecodealliance/jco.git) +is about as fast as Goja - PocketBase's JS interpreter - but about 40x slower +than V8. +On the other hand, Rust compiled to WASM is almost 4x faster than V8 with more +predictable latency and a lower resource footprint. +A benefit of the new runtime integration is that different endpoints can be +implemented in different languages providing extra flexibility and potential to +optimize. + + +## Migration + +The first difference you'll encounter is the need for a build-step: JS/TS -> WASM[^5]. +For now, we recommend to copy the template in `examples/wasm-guest-ts` or +`examples/wasm-guest-ts`, dependning on whether you prefer TypeScript or +JavaScript respectively. +You can then simply run `pnpm install && pnpm build` to build the WASM component. +For TrailBase to pick up you `*.wasm` component, they need to be placed in +`/wasm/`. + +The second big difference you'll notice right away is that the APIs for +registering endpoints had to change to work in the context of the short-lived +and isolated runtime instances. +Previously we were relying on global state for routing. +To avoid re-initialization on every request and for consistency with guest +languages that do not support eager initialization of globals, we're now +using module exports. + +TypeScript endpoint before: + +```ts +addRoute( + "GET", + "/test", + stringHandler(async (req: StringRequestType) => { + const uri: ParsedPath = parsePath(req.uri); + + const table = uri.query.get("table"); + if (table) { + const rows = await query(`SELECT COUNT(*) FROM "${table}"`, []); + return `entries: ${rows[0][0]}`; + } + + return `test: ${req.uri}`; + } +``` + +and after: + +```ts +export default defineConfig({ + httpHandlers: [ + HttpHandler.get("/test", (req: Request) : string => { + const table = uri.getQueryParam("table"); + if (table) { + const rows = await query(`SELECT COUNT(*) FROM "${table}"`, []); + return `entries: ${rows[0][0]}`; + } + + return `test: ${req.url()}`; + }, + ], +}); +``` + +Alternatively in Rust: + +```rust +use trailbase_wasm::db::{query, Value}; +use trailbase_wasm::http::{HttpError, HttpRoute, StatusCode, routing}; +use trailbase_wasm::{Guest, export}; + +struct Endpoints; + +impl Guest for Endpoints { + fn http_handlers() -> Vec { + return vec![ + routing::get("/test", async |req| { + let Some(table) = req.query_param("table") else { + return Ok(format!("test: {:?}", req.url())); + }; + + let rows = query(format!("SELECT COUNT(*) FROM '{table}'"), []) + .await + .map_err(|err| HttpError::message(StatusCode::INTERNAL_SERVER_ERROR, err))?; + + return Ok(format!("entries: {:?}", rows[0][0])); + }), + ]; + } +} + +export!(Endpoints); +``` + +See `/examples/wasm-guest-(js|rust|ts)` for further examples while we continue +to improve the documentation. + +## Next-Steps + +First and foremost, we'd love to hear from you, make the transition as smooth +as possible and the WASM runtime best-in-class 🙏. + +Once the new runtime integration has seen more mileage and unforeseen surprises +are worked out, we'd like to sunset V8 expediently. +This will provide immediate benefits in terms of portability, security, +build-times and binary sizes. + +From that point on we plan to invest heavily into making the integration the +best we can. +With the previous V8 integration, given its rough edges and unstable APIs, we +were unsure and limiting the effort. +The plans is to support a wider range of extension points and guest languages, +thus supporting more use-cases and making it suitable for wider range of +developers. +If you think there's any language that would be particularly valuable, e.g. due +to its unique and important ecosystem, let us know. +When WASIp3 becomes available, hopefully in the near future, we're also +planning to transparently upgrade making asynchronous interactions between host +and guest more of a first-class citizen. + +Thank you for making it this far and your time 🙏. + +--- + +[^1]: + GLIBC static binaries aren't really static. + +[^2]: + As system to express cross-component interfaces for WASM in a + language-agnostic manner. It's like gRPC but FFI, i.e. no I/O + thus allowing both synchronous and asynchronous interactions. + +[^3]: + That said, bundling the interpreter with static input unlocks some + optimizations, e.g. + [Futamura projection using weval](https://github.com/bytecodealliance/weval). + +[^4]: + For state sharing between requests you'll need should rely on SQLite or + KVStore. Note that even with long-lived V8 isolates that was already the case, + since state was only shared coincidentally within the same isolate, i.e. + subsequent requests may or may not be able to see that state. + +[^5]: + A long-standing feature request for us has been to support hot-restart when + components change. We're still planning to get there. A separate watcher + process would re-build the WASM component and signal the `trail` binary to + reload the WASM component. diff --git a/docs/src/content/docs/comparison/pocketbase.mdx b/docs/src/content/docs/comparison/pocketbase.mdx index 56ced8b8..48e3c1ab 100644 --- a/docs/src/content/docs/comparison/pocketbase.mdx +++ b/docs/src/content/docs/comparison/pocketbase.mdx @@ -14,7 +14,7 @@ experience. Gani, the person behind it, is a mad scientist 🙏. From a distance PocketBase and TrailBase are both single-executables providing almost identical feature sets: REST APIs, realtime updates, authentication, file -storage, JavaScript (JS) runtimes, admin dashboard..., all on top of SQLite. +storage, a runtime for custom endpoints, admin dashboard..., all on top of SQLite. For the sake of this comparison, we'll dive a little deeper to have a closer look at their differences both technically and philosophically. @@ -59,8 +59,11 @@ Measuring we found that TrailBase's APIs are roughly [10x faster](/reference/ben This may sound like a lot but is the result of SQLite itself being extremely fast meaning that even small overheads weigh heavily. -Independently, TrailBase choice of V8 as its JS runtime allows code to run -roughly 40x faster. +Independently, TrailBase offers a WebAssembly runtime over PocketBase's +JavaScript interpreter. +This allows for strict state isolation between requests and supports a +wide-range of guest languages - currently JS/TS and Rust with more to come. +A custom Rust endpoint can be up to 140x faster than a PocketBase JS one. ### Framework Use @@ -94,10 +97,8 @@ sleeve: - Language independent bindings via JSON-schema with strict type-safety being enforced from the client all the way to the database[^4]. -- A more Node-like JS runtime with full ES6 support, built-in TypeScript - transpilation, and V8 performance unlocking more of the JS ecosystem and enabling - server-side rendering (SSR) - with any popular JS framework. +- A WASM runtime, supporting multiple guest languages for custom endpoints and + up to 140x speed-up[^5]. - Untethered access to SQLite with all its features and capabilities. - A wider set of first-class client libraries beyond JS/TS and Dart, including C#, Python and Rust. @@ -154,3 +155,8 @@ flexibility and performance matter. [^4]: Note that SQLite is not strictly typed by default. Instead column types merely a type affinity for value conversions. + +[^5]: + Depends a lot on the guest language and the specific taks in question. 140x + is what we measured in heavy computational tasks comparing Goja with + Rust->WASM. It's apples to oranges, yet practically applicable. diff --git a/docs/src/content/docs/documentation/apis_js.mdx b/docs/src/content/docs/documentation/apis_js.mdx index 2a63dff4..22e60f19 100644 --- a/docs/src/content/docs/documentation/apis_js.mdx +++ b/docs/src/content/docs/documentation/apis_js.mdx @@ -1,57 +1,41 @@ --- -title: JS/TS APIs +title: Custom APIs --- import { Aside } from "@astrojs/starlight/components"; -On startup TrailBase will automatically load any JavaScript and TypeScript -files in `traildepot/scripts`. -This can be used to implement arbitrary HTTP APIs using custom handlers. +On startup TrailBase will automatically load any WASM component, i.e. `*.wasm` +files in `traildepot/wasm`. +This can be used to implement arbitrary HTTP APIs with custom handlers. ## Example HTTP Endpoint -The following example illustrates a few things: +The following TypeScript WASM example illustrates a few things: * How to register a parameterized route with `{table}`. -* How to implement a handler that returns `text/plain` content. There is also - `jsonHandler` and `htmlHandler`. * How to query the database. -* How to return an error. +* How to return an HTTP error. -```js -import { - addRoute, - query, - stringHandler, - HttpError, - StatusCodes -} from "../trailbase.js"; +```ts +import { defineConfig } from "trailbase-wasm"; +import { Request, HttpError, HttpHandler, StatusCode } from "trailbase-wasm/http"; +import { query } from "trailbase-wasm/db"; -addRoute("GET", "/test/{table}", stringHandler(async (req) => { - const table = req.params["table"]; +async function handler(req: Request): Promise { + const table = req.getPathParam("table"); if (table) { const rows = await query(`SELECT COUNT(*) FROM ${table}`, []) return `entries: ${rows[0][0]}`; } throw new HttpError( - StatusCodes.BAD_REQUEST, "Missing '?table=' search query param"); -})); + StatusCode.BAD_REQUEST, "Missing '?table=' search query param"); +} + +export default defineConfig({ + httpHandlers: [ HttpHandler.get("/test/{table}", handler) ], +}); ``` -More examples can be found in the repository in -`client/testfixture/scripts/index.ts`. - -## Runtime Details - -At its heart, TrailBase's runtime is a pool of V8 worker threads - called -*isolates* - alongside a runtime that supports basic tasks such as file I/O, web -requests, timers, etc. -While it uses Deno's V8-integration under the hood, it is *not* a full Node.js -runtime, at least for now. - - +More examples can be found in the repository under `examples/wasm-guest-ts/`, +`examples/wasm-guest-js/` and `examples/wasm-guest-rust/`. diff --git a/docs/src/content/docs/getting-started/first-ui-app.mdx b/docs/src/content/docs/getting-started/first-ui-app.mdx index e8fe82f4..b414a643 100644 --- a/docs/src/content/docs/getting-started/first-ui-app.mdx +++ b/docs/src/content/docs/getting-started/first-ui-app.mdx @@ -3,7 +3,7 @@ title: > TypeScript API, Vector Search & UI --- -import { Aside, Code } from "@astrojs/starlight/components"; +import { Aside, Code, Tabs, TabItem } from '@astrojs/starlight/components'; import { githubPath } from "@/lib/github"; import { repo } from "@/config"; @@ -84,30 +84,38 @@ UPDATE coffee SET embedding = vec_f32(FORMAT("[%f, %f, %f, %f]", Aroma, Flavor, initializing the previously skipped `coffee.embedding` for all records. -## Custom TypeScript Endpoint +## Custom Endpoints -Any time you start `trail run`[^3], JavaScript and TypeScript files under -`traildepot/scripts` will be executed. +Any time you start `trail run`[^3], WebAssembly components, i.e. `.wasm` files, +under `traildepot/wasm` will be compiled and initialized. - +We can use this to declare custom HTTP API routes among other things. +Let's have a quick look at `examples/coffee-vector-search/guests`, +which define a `/search` API route using vector search, which we'll later need +to find coffees most closely matching our desired coffee: -We can use this to register custom HTTP API routes among other things. -Let's have a quick look at `examples/coffee-vector-search/traildepot/scripts/main.ts`, -which defines a `/search` API route we'll later use in our application to -find coffees most closely matching our desired coffee notes: +import handlerTsCode from "@root/examples/coffee-vector-search/guests/typescript/src/index.ts?raw"; +import handlerRustCode from "@root/examples/coffee-vector-search/guests/rust/src/lib.rs?raw"; -import handlerCode from "@root/examples/coffee-vector-search/traildepot/scripts.delme/main.ts?raw"; + + + + - + + + + While `trail run` is up, we can test the public `/search` endpoint simply by running: diff --git a/docs/src/content/docs/index.mdx b/docs/src/content/docs/index.mdx index a130eba9..101a2cb8 100644 --- a/docs/src/content/docs/index.mdx +++ b/docs/src/content/docs/index.mdx @@ -55,15 +55,11 @@ export const demoLink = "https://demo.trailbase.io"; * Rust: one of the lowest overhead languages, * Axum: one of the fastest HTTP servers, * SQLite: one of the fastest full-SQL databases, - * V8: one of the fastest JS engines. + * Wasmtime: compiles custom endpoints to efficient native code. TrailBase's APIs are [11x faster than PocketBase's and almost 40x faster than SupaBase's with a fraction of the footprint](/reference/benchmarks) allowing you to serve millions of customers from a tiny box. - - In terms of JS/TS performance, V8 is roughly - [40x faster](/reference/benchmarks#javascript) - than goja used by PocketBase.
@@ -184,8 +180,8 @@ export const demoLink = "https://demo.trailbase.io"; Provide access to your tables and views through fast, flexible and **type-safe** restful CRUD APIs. - Listen for data changes with realtime APIs and extend functionality - using a fast V8 JS/ES6 runtime with built-in support for TypeScript. + Listen for data changes with realtime APIs and extend functionality using + a fast WebAssembly runtime with support for many guest languages. Authorize users based on ACLs and SQL access rules letting you easily build higher-level access management or moderation facilities diff --git a/docs/src/content/docs/reference/_benchmarks/wasm_runtime.tsx b/docs/src/content/docs/reference/_benchmarks/wasm_runtime.tsx new file mode 120000 index 00000000..973ecf16 --- /dev/null +++ b/docs/src/content/docs/reference/_benchmarks/wasm_runtime.tsx @@ -0,0 +1 @@ +../../../blog/_switching_to_a_wasm_runtime.tsx \ No newline at end of file diff --git a/docs/src/content/docs/reference/benchmarks.mdx b/docs/src/content/docs/reference/benchmarks.mdx index cbeacb09..4e01ae7b 100644 --- a/docs/src/content/docs/reference/benchmarks.mdx +++ b/docs/src/content/docs/reference/benchmarks.mdx @@ -187,6 +187,28 @@ being roughly 3 times slower than p50. Slower insertions can take north of 100ms. This may be related to GC pauses, scheduling, or more generally the same CPU variability we observed earlier. +## Runtimes + +[TrailBase is currently going through a transition from a V8-based runtime to a +WASM one](/blog/switching_to_a_wasm_runtime). + +V8's execution with just-in-time (JIT) compilation is quite speedy and is about +40x faster than Goja, PocketBase's interpreter and similar other interpreters +(the graph's y-axis is logarithmic). +With the new WASM-based runtime, execution of JS relies on bundling an +interpreter and thus performance has regressed to be roughly on-par with Goja +and other JIT-less engines. +However, guests languages that can compile natively to WASM, e.g. Rust, can shine +with roughly a 135x speed-up with strong state isolation between requests. + +import { RuntimeFib40Times } from "./_benchmarks/wasm_runtime.tsx"; + +
+
+ +
+
+ ## File System File systems play an important role for the performance of storage systems, @@ -230,6 +252,8 @@ disc space and feature sets. For example, CoW snapshots may or may not be important to you. +{/* + ## JavaScript The benchmark sets up a custom HTTP endpoint `/fibonacci?n=` using the same @@ -243,12 +267,6 @@ In other words, the impact of any overhead within PocketBase or TrailBase is diminished by the time it takes to compute `fibonacci(N)` for sufficiently large `N`. -{/* -Output: - TB: Called "/fibonacci" for fib(40) 100 times, took 0:00:14.988703 (limit=64) - PB: Called "/fibonacci" for fib(40) 100 times, took 0:10:01.096053 (limit=64) -*/} - We found that for `N=40`, V8 (TrailBase) is around 40 times faster than goja (PocketBase): @@ -270,7 +288,15 @@ With the addition of V8 to TrailBase, we've experienced a significant increase in the memory baseline dominating the overall footprint. In this setup, TrailBase consumes roughly 4 times more memory than PocketBase. If memory footprint is a major concern for you, constraining the number of V8 -threads will be an effective remedy (`--js-runtime-threads`). +threads will be an effective remedy (`--runtime-threads`). + +*/} + +{/* +Output: + TB: Called "/fibonacci" for fib(40) 100 times, took 0:00:14.988703 (limit=64) + PB: Called "/fibonacci" for fib(40) 100 times, took 0:10:01.096053 (limit=64) +*/} ## Final Words diff --git a/docs/src/pages/blog.astro b/docs/src/pages/blog.astro index aceea96a..3de48a87 100644 --- a/docs/src/pages/blog.astro +++ b/docs/src/pages/blog.astro @@ -31,7 +31,7 @@ async function getPosts() { const posts = blogPosts ?? (await getPosts()); --- - +
{ posts.map((post, index: number) => { diff --git a/docs/src/pages/blog/[slug].astro b/docs/src/pages/blog/[slug].astro index 21ad460c..edc3f3f9 100644 --- a/docs/src/pages/blog/[slug].astro +++ b/docs/src/pages/blog/[slug].astro @@ -66,7 +66,7 @@ const proseStyle: string[] = [ ]; --- - +
{/* Header stuff: title, subtitle, tags, read time, ... */}
@@ -80,7 +80,7 @@ const proseStyle: string[] = [ - ~{Math.floor(readMinutes) + 1} minutes + ~{Math.floor(readMinutes) + 1} minute read