Skip to content

Anole 0.3.0

Choose a tag to compare

@xuckless xuckless released this 23 Oct 17:51
· 611 commits to main since this release

Anole 0.3.0 is the developer-experience release. My goal for it was that starting, configuring, testing and watching an Anole app feels as easy as it does on Express, Fiber or Bun.

zig fetch --save git+https://github.com/xuckless/anole#v0.3.0

What's new

  • Settings, .env files and deployment modes. anole.config.load(T, ...) reads typed settings from defaults, files and the environment. .env files are read in development. environment (development or production) and deployment (standalone, behind_proxy, anole_lb, platform) set sensible defaults that any explicit option overrides, and the server prints what they changed at startup.
  • Validation and typed handlers. Field rules on structs, all violations reported at once as a 422 problem+json from req.bind, and anole.In(.{ .path, .query, .headers, .body }) for typed handler inputs. Route paths are checked at compile time.
  • Logging. JSON Lines logs by default in production, readable logs in development, and startup errors as two readable lines: what failed and how to fix it.
  • Testing. anole.testing gives an in-memory client for routes and middleware.
  • Resilience. Retries with backoff and jitter, retry budgets, and circuit breakers. PostgreSQL, Redis and MongoDB calls fail fast with 503 while their breaker is open.
  • Messaging. A NATS client and my own Kafka client (produce, consumer groups, compression), both also in the C ABI.
  • OpenAPI. An OpenAPI 3.1 document generated from typed routes, validator rules and protobuf, for REST, transcoded gRPC and Connect.
  • The anole command line. anole new scaffolds a project from templates (minimal, rest-postgres, realtime, grpc, full). Prebuilt binaries for five platforms are attached below, with SHA256SUMS.
  • A live console. anole console attaches to a running app over a local socket and shows logs, requests, errors, connections, databases, WebSocket and HTTP/3 state, config and routes, and can reload or drain the app. It's on by default and costs nothing while nobody is attached.
  • Runtime gauges per executor in /metrics.
  • C ABI 0.9, with everything above that a Node or Python app would use.

Breaking changes

  • req.bind validates by default: a JSON body whose values don't fit the type is now a 422 with the violations, not a 400. req.bindWith keeps the old behaviour.
  • Route methods take their path at compile time. Use app.addRoute(method, path, handler) for a path known only at run time.
  • WebSocket over HTTP/2 (RFC 8441) is now off by default. In my measurements it used about twice the CPU of WebSocket over HTTP/1.1 (#91). Turn it on with http2.enable_connect_protocol = true.
  • timeouts.keepalive defaults to 75 s, up from 60 s, so it sits above the idle timeout of nginx and AWS load balancers.
  • The access log's JSON lines follow the shared log record (time replaces ts).

docs/migrating.md has every change with before and after code.

Performance

I compared 0.3.0 with 0.2.1 on my x86 Linux box: three rested rounds per case in rotating order. HTTP/1.1, HTTP/2, HTTP/3, PostgreSQL and WebSocket are all within noise of 0.2.1, and instructions per HTTP/1.1 request are within 0.4%. With the console enabled but nobody attached, HTTP/3 on io_uring is about 1.7% slower; I'll follow that up in #103.

Fixed along the way

  • A NATS connect() with reconnect off could report a failure although the connection had been made, when the server hung up right after the handshake.
  • Kafka now works with brokers that serve Produce v7 and ListOffsets v4, such as Redpanda 25.2.

Known issues

  • On Windows, a development-only target, one test still hangs. I'll fix it in 0.3.1, together with an HTTP/3 client stall after a NAT rebinding mid-download (#98).

The full list is in CHANGELOG.md.