Update documentation to cover the new WASM runtime.

This commit is contained in:
Sebastian Jeltsch
2025-09-09 16:25:12 +02:00
parent b4f651498b
commit 5dbe253cb0
16 changed files with 477 additions and 88 deletions

View File

@@ -9,7 +9,7 @@
<p align="center"> <p align="center">
An open, <a href="https://trailbase.io/reference/benchmarks/">blazingly fast</a>, An open, <a href="https://trailbase.io/reference/benchmarks/">blazingly fast</a>,
single-executable Firebase alternative with type-safe REST & realtime APIs, built-in JS/ES6/TS 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.
<p> <p>
<p align="center"> <p align="center">

View File

@@ -68,6 +68,7 @@ export default defineConfig({
"http://localhost:4000/**/*", "http://localhost:4000/**/*",
// The link validator fails to validate the OpenAPI pages injected above. // The link validator fails to validate the OpenAPI pages injected above.
`/${openApiBase}/**/*`, `/${openApiBase}/**/*`,
"/blog/**/*",
], ],
}), }),
], ],

View File

@@ -17,7 +17,9 @@ Chart.register(BarWithErrorBarsController, BarWithErrorBar, ChartDeferred);
interface BarChartProps { interface BarChartProps {
data: ChartData<"bar">; data: ChartData<"bar">;
scales?: { [key: string]: ScaleOptions<"linear"> }; scales?: {
[key: string]: ScaleOptions<"linear"> | ScaleOptions<"logarithmic">;
};
} }
export function BarChart(props: BarChartProps) { export function BarChart(props: BarChartProps) {

View File

@@ -59,7 +59,7 @@ if (image) {
<p> <p>
An open, blazingly fast, single-executable Firebase alternative with An open, blazingly fast, single-executable Firebase alternative with
type-safe REST & realtime APIs, built-in JS/ES6/TS runtime, SSR, auth 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.
</p> </p>
<br /> <br />

View File

@@ -139,11 +139,9 @@ const href = pathWithBase(Astro.props.locale || "/");
), ),
)} )}
{import.meta.env.DEV && ( <a class="site-title-link" href="/blog">
<a class="site-title-link" href="/blog"> Blog
Blog </a>
</a>
)}
</div> </div>
</div> </div>
) )

View File

@@ -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 (
<BarChart
data={data}
scales={{
y: {
type: "logarithmic",
title: {
display: true,
text: "Time [s]",
},
},
}}
/>
);
}

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created by AtomCrusher for the English Wikipedia -->
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="612" height="612">
<!-- Block -->
<path d="m376 0c0 1.08 0 2.16 0 3.3 0 38.76-31.42 70.17-70.17 70.17-38.76 0-70.17-31.42-70.17-70.17l0 0c0-1.14 0-2.22 0-3.3L0 0l0 612 612 0 0-612z" fill="#654ff0"/>
<!-- Letters -->
<path d="m142.16 329.81 40.56 0 27.69 147.47 0.5 0 33.28-147.47 37.94 0 30.06 149.28 0.59 0 31.56-149.28 39.78 0-51.69 216.69-40.25 0-29.81-147.47-0.78 0-31.91 147.47-41 0zm287.69 0 63.94 0 63.5 216.69-41.84 0-13.81-48.22-72.84 0-10.66 48.22-40.75 0zm24.34 53.41-17.69 79.5 55.06 0-20.31-79.5z" fill="#fff"/>
</svg>

After

Width:  |  Height:  |  Size: 710 B

View File

@@ -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.
<div class="flex justify-center">
<div class="h-[360px] w-[70%]">
<RuntimeFib40Times client:only="solid-js" />
</div>
</div>
### 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
`<traildepot>/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<HttpRoute> {
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.

View File

@@ -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 From a distance PocketBase and TrailBase are both single-executables providing
almost identical feature sets: REST APIs, realtime updates, authentication, file 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 For the sake of this comparison, we'll dive a little deeper to have a closer
look at their differences both technically and philosophically. 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 This may sound like a lot but is the result of SQLite itself being extremely
fast meaning that even small overheads weigh heavily. fast meaning that even small overheads weigh heavily.
Independently, TrailBase choice of V8 as its JS runtime allows code to run Independently, TrailBase offers a WebAssembly runtime over PocketBase's
roughly 40x faster. 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 ### Framework Use
@@ -94,10 +97,8 @@ sleeve:
- Language independent bindings via JSON-schema with strict type-safety - Language independent bindings via JSON-schema with strict type-safety
being enforced from the client all the way to the database[^4]. 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 - A WASM runtime, supporting multiple guest languages for custom endpoints and
transpilation, and V8 performance unlocking more of the JS ecosystem and enabling up to 140x speed-up[^5].
<a href={githubPath("examples/collab-clicker-ssr")}>server-side rendering (SSR)</a>
with any popular JS framework.
- Untethered access to SQLite with all its features and capabilities. - Untethered access to SQLite with all its features and capabilities.
- A wider set of first-class client libraries beyond JS/TS and Dart, including - A wider set of first-class client libraries beyond JS/TS and Dart, including
C#, Python and Rust. C#, Python and Rust.
@@ -154,3 +155,8 @@ flexibility and performance matter.
[^4]: [^4]:
Note that SQLite is not strictly typed by default. Instead column types Note that SQLite is not strictly typed by default. Instead column types
merely a type affinity for value conversions. 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.

View File

@@ -1,57 +1,41 @@
--- ---
title: JS/TS APIs title: Custom APIs
--- ---
import { Aside } from "@astrojs/starlight/components"; import { Aside } from "@astrojs/starlight/components";
On startup TrailBase will automatically load any JavaScript and TypeScript On startup TrailBase will automatically load any WASM component, i.e. `*.wasm`
files in `traildepot/scripts`. files in `traildepot/wasm`.
This can be used to implement arbitrary HTTP APIs using custom handlers. This can be used to implement arbitrary HTTP APIs with custom handlers.
## Example HTTP Endpoint ## 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 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 query the database.
* How to return an error. * How to return an HTTP error.
```js ```ts
import { import { defineConfig } from "trailbase-wasm";
addRoute, import { Request, HttpError, HttpHandler, StatusCode } from "trailbase-wasm/http";
query, import { query } from "trailbase-wasm/db";
stringHandler,
HttpError,
StatusCodes
} from "../trailbase.js";
addRoute("GET", "/test/{table}", stringHandler(async (req) => { async function handler(req: Request): Promise<string> {
const table = req.params["table"]; const table = req.getPathParam("table");
if (table) { if (table) {
const rows = await query(`SELECT COUNT(*) FROM ${table}`, []) const rows = await query(`SELECT COUNT(*) FROM ${table}`, [])
return `entries: ${rows[0][0]}`; return `entries: ${rows[0][0]}`;
} }
throw new HttpError( 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 More examples can be found in the repository under `examples/wasm-guest-ts/`,
`client/testfixture/scripts/index.ts`. `examples/wasm-guest-js/` and `examples/wasm-guest-rust/`.
## 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.
<Aside type="note" title="State Sharing">
Different *isolates* do not share state, i.e. you cannot use global state to
reliably share state across requests. Instead, state can be persisted using
the database.
</Aside>

View File

@@ -3,7 +3,7 @@ title: >
TypeScript API, Vector Search & UI 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 { githubPath } from "@/lib/github";
import { repo } from "@/config"; 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. 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 Any time you start `trail run`[^3], WebAssembly components, i.e. `.wasm` files,
`traildepot/scripts` will be executed. under `traildepot/wasm` will be compiled and initialized.
<Aside type="note"> We can use this to declare custom HTTP API routes among other things.
TrailBase will automatically transpile TypeScript to JavaScript which can Let's have a quick look at `examples/coffee-vector-search/guests`,
then execute on the underlying V8 engine. You don't need a separate build which define a `/search` API route using vector search, which we'll later need
step. to find coffees most closely matching our desired coffee:
</Aside>
We can use this to register custom HTTP API routes among other things. import handlerTsCode from "@root/examples/coffee-vector-search/guests/typescript/src/index.ts?raw";
Let's have a quick look at `examples/coffee-vector-search/traildepot/scripts/main.ts`, import handlerRustCode from "@root/examples/coffee-vector-search/guests/rust/src/lib.rs?raw";
which defines a `/search` API route we'll later use in our application to
find coffees most closely matching our desired coffee notes:
import handlerCode from "@root/examples/coffee-vector-search/traildepot/scripts.delme/main.ts?raw"; <Tabs>
<TabItem label="TypeScript">
<Code
code={handlerTsCode}
lang="ts"
title={"examples/coffee-vector-search/guests/typescript/src/index.ts"}
mark={[]}
/>
</TabItem>
<Code <TabItem label="Rust">
code={handlerCode} <Code
lang="ts" code={handlerRustCode}
title={"examples/coffee-vector-search/traildepot/scripts/main.ts"} lang="rust"
mark={[]} title={"examples/coffee-vector-search/guests/rust/src/lib.rs"}
/> mark={[]}
/>
</TabItem>
</Tabs>
While `trail run` is up, we can test the public `/search` endpoint simply by While `trail run` is up, we can test the public `/search` endpoint simply by
running: running:

View File

@@ -55,15 +55,11 @@ export const demoLink = "https://demo.trailbase.io";
* Rust: one of the lowest overhead languages, * Rust: one of the lowest overhead languages,
* Axum: one of the fastest HTTP servers, * Axum: one of the fastest HTTP servers,
* SQLite: one of the fastest full-SQL databases, * 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 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 with a fraction of the footprint](/reference/benchmarks) allowing you to
serve millions of customers from a tiny box. 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.
</div> </div>
<div slot="second"> <div slot="second">
@@ -184,8 +180,8 @@ export const demoLink = "https://demo.trailbase.io";
<Card title="APIs & File Storage" icon="random"> <Card title="APIs & File Storage" icon="random">
Provide access to your tables and views through fast, flexible and Provide access to your tables and views through fast, flexible and
**type-safe** restful CRUD APIs. **type-safe** restful CRUD APIs.
Listen for data changes with realtime APIs and extend functionality Listen for data changes with realtime APIs and extend functionality using
using a fast V8 JS/ES6 runtime with built-in support for TypeScript. a fast WebAssembly runtime with support for many guest languages.
Authorize users based on ACLs and SQL access rules letting you Authorize users based on ACLs and SQL access rules letting you
easily build higher-level access management or moderation facilities easily build higher-level access management or moderation facilities

View File

@@ -0,0 +1 @@
../../../blog/_switching_to_a_wasm_runtime.tsx

View File

@@ -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, 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. 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";
<div class="flex justify-center">
<div class="h-[300px] w-[90%]">
<RuntimeFib40Times client:only="solid-js" />
</div>
</div>
## File System ## File System
File systems play an important role for the performance of storage systems, 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. important to you.
{/*
## JavaScript ## JavaScript
The benchmark sets up a custom HTTP endpoint `/fibonacci?n=<N>` using the same The benchmark sets up a custom HTTP endpoint `/fibonacci?n=<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 diminished by the time it takes to compute `fibonacci(N)` for sufficiently
large `N`. 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 We found that for `N=40`, V8 (TrailBase) is around 40 times faster than
goja (PocketBase): 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 the memory baseline dominating the overall footprint.
In this setup, TrailBase consumes roughly 4 times more memory than PocketBase. 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 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 ## Final Words

View File

@@ -31,7 +31,7 @@ async function getPosts() {
const posts = blogPosts ?? (await getPosts()); const posts = blogPosts ?? (await getPosts());
--- ---
<Base title="Updates" draft={true}> <Base title="Updates">
<section class="container mx-auto mb-10 max-w-screen-lg px-7 py-4"> <section class="container mx-auto mb-10 max-w-screen-lg px-7 py-4">
{ {
posts.map((post, index: number) => { posts.map((post, index: number) => {

View File

@@ -66,7 +66,7 @@ const proseStyle: string[] = [
]; ];
--- ---
<Base title={entry.data.title} description={entry.data.intro} draft={true}> <Base title={entry.data.title} description={entry.data.intro}>
<article> <article>
{/* Header stuff: title, subtitle, tags, read time, ... */} {/* Header stuff: title, subtitle, tags, read time, ... */}
<div class:list={[...proseStyle]}> <div class:list={[...proseStyle]}>
@@ -80,7 +80,7 @@ const proseStyle: string[] = [
<PublishDate date={entry.data.pubDate} /> <PublishDate date={entry.data.pubDate} />
<span class:list={["text-[16px]", "transition-all", "duration-300"]}> <span class:list={["text-[16px]", "transition-all", "duration-300"]}>
~{Math.floor(readMinutes) + 1} minutes ~{Math.floor(readMinutes) + 1} minute read
</span> </span>
</div> </div>