caddymodule

package module
v0.1.57 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 12, 2026 License: Apache-2.0 Imports: 14 Imported by: 0

README

hazedb as a Caddy module

This package wraps hazedb as a Caddy HTTP handler, so a single Caddy binary serves your site and an in-process SQL store — no separate database process, no network hop.

It exposes five endpoints. POST /query and POST /exec take a JSON body {"sql": "...", "args": [...]}; the GET routes take URL parameters.

Endpoint Runs Returns
POST /query ad-hoc SELECT (incl. JOINs) {"columns":[...],"rows":[[...],...]}
POST /exec INSERT / UPDATE / DELETE / CREATE TABLE / DROP TABLE {"affected":N}
GET /get?table=T&id=UUID (or &col=C&val=V) [&cols=a,b] one-row read (PK or indexed-column fast path) one JSON object, or null
GET /list?table=T [&cols=a,b][&col=C&val=V][&limit=N] multi-row read [{...},...]
GET /meta store-size overview {"tables":N,"max_bytes":M,"total_rows":R,"total_approx_bytes":B,"table_stats":[{name,rows,columns,indexes,approx_bytes},...]}

GET /meta takes no parameters; it reports the table count, the configured max_bytes cap (0 = unlimited), the store-wide total_rows / total_approx_bytes, and per table the row / column / index counts and an approximate in-RAM byte size — for dashboards and health checks. The byte sizes are estimates (cell payloads plus modeled overhead, biased slightly high), not exact accounting.

Byte cap. Set max_bytes (below) to bound the store's RAM. An INSERT that would push total_approx_bytes past the cap is rejected with HTTP 507 (Insufficient Storage); the store never auto-evicts, so the client frees space with DELETE / DROP TABLE. total_approx_bytes vs max_bytes from /meta is the headroom gauge.

args is an optional positional list for ? placeholders. JSON → SQL value mapping: number → INT, bool → BOOL, null → NULL, string → STRING, except a string in canonical UUID form (8-4-4-4-12 hex) → UUID — so you can address and insert UUID columns from JSON.

Build a Caddy binary with the module

Uses xcaddy. Until the core hazedb version that ships db_registry.go + wire.go is tagged, build against the local checkout (the submodule's replace points at ../):

# from a checkout of this repo
xcaddy build \
    --with github.com/VeloxCoding/hazedb/caddymodule=./caddymodule \
    --with github.com/VeloxCoding/hazedb=.

After the core is published, the plain form works:

xcaddy build --with github.com/VeloxCoding/hazedb/caddymodule

Configure (Caddyfile)

:8080 {
    handle /db/* {
        hazedb {
            wal_level      1                        # 0 memory-only, 1 background fsync, 2 fsync-per-write
            wal_path       /var/lib/hazedb/wal      # directory for the WAL segments (required when wal_level > 0)
            wal_rotation   5s                       # how often the active segment is sealed (default 5s)
            sqlite_path    /var/lib/hazedb/hazedb.db # on-disk SQLite mirror (recovery source; requires wal_level > 0)
            init_sql       /etc/hazedb/schema.sql   # CREATE TABLE + seed, run once at startup
            registry_name  default                  # name the *DB is published under for the PHP extension
            max_body_bytes 4194304                  # POST body cap for /query and /exec (default 4 MiB)
            max_bytes      1073741824               # cap the store's RAM (1 GiB); over-cap INSERT → HTTP 507. 0/unset = unlimited
        }
    }
}

All subdirectives are optional. With wal_level unset (or 0) the store is memory-only and wal_path is not needed. Tables are created at runtime: put your CREATE TABLE statements in the init_sql file, or POST them to /exec.

Schema / tables

hazedb creates tables at runtime, so the module opens with an empty schema. Define tables one of two ways:

  • init_sql — a file of ;-separated statements run once at startup (typical: CREATE TABLE ... plus any seed rows). Don't put a ; inside a string literal in that file.
  • POST /exec — send CREATE TABLE ... like any other write.

Sharing the instance with PHP

The module publishes its *DB in hazedb's process-wide registry under registry_name (default "default"). The FrankenPHP/PHP extension (addons/frankenphp-ext) looks up that same name, so PHP calls and HTTP calls hit one identical database — no second copy.

WAL + config reload caveat

With wal_path set, a Caddy graceful config reload runs the new handler's Provision (which opens the WAL file) before the old handler's Cleanup (which closes it) — briefly two writers on one file. Memory mode reloads cleanly. For durable deployments, prefer a full restart over a graceful reload when changing this handler.

Documentation

Overview

Package caddymodule exposes hazedb as a Caddy HTTP handler.

The core hazedb package stays Caddy-free; this adapter owns the transport: it opens a *DB, serves GET /get (single-row read), GET /list (multi-row read), POST /query, POST /exec, and GET /meta (store-size overview) over an internal mux, and registers the *DB under a name in the core registry so an in-process consumer (the FrankenPHP/PHP extension) reaches the very same instance. Per the gateway boundary in the RFC, request-context cross-cutting concerns (auth, per-tenant routing, rate limits) belong here, never in core.

Schema: the module opens with an empty schema by default. hazedb supports runtime CREATE TABLE, so operators define tables via an init_sql file (run once at Provision) or by POSTing DDL to /exec.

WAL + reload caveat: with wal_path set, Caddy config reload runs the new Provision (which opens the same file) before the old Cleanup (which closes it) — two writers on one WAL file for that window. Memory mode (no wal_path) reloads cleanly. For durable deployments, restart rather than graceful-reload when changing this handler.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Handler

type Handler struct {
	// WALLevel is the durability switch: 0 = memory only, 1 = background fsync
	// (~1s window), 2 = fsync every write (slowest, safest). Above 0, WALPath
	// is required.
	WALLevel int `json:"wal_level,omitempty"`
	// WALPath is the directory holding the write-ahead log segments. Required
	// when wal_level > 0.
	WALPath string `json:"wal_path,omitempty"`
	// WALRotateMillis is how often the active segment is sealed, in ms. 0 = 5s.
	WALRotateMillis int `json:"wal_rotate_ms,omitempty"`
	// SQLitePath enables the on-disk SQLite mirror at this path (system of record
	// + recovery source). Empty = no mirror. Requires wal_level > 0.
	SQLitePath string `json:"sqlite_path,omitempty"`
	// InitSQL is an absolute path to a .sql file run once at Provision, before
	// Caddy serves — typically CREATE TABLE + seed rows. Statements are split on
	// ';'; do not put a semicolon inside a string literal in this file.
	InitSQL string `json:"init_sql,omitempty"`
	// RegistryName is the name the *DB is published under for in-process
	// consumers. Empty = "default" (what the PHP extension looks up).
	RegistryName string `json:"registry_name,omitempty"`
	// MaxBodyBytes caps the POST body for /query and /exec, in bytes. 0 = the
	// 4 MiB default. Bounds per-request memory against an oversized body — and
	// since the SQL string and the positional-arg array both live in that body,
	// it bounds their sizes too.
	MaxBodyBytes int64 `json:"max_body_bytes,omitempty"`
	// MaxBytes caps the store's total in-RAM size, in bytes. 0 = unlimited. An
	// INSERT that would push the store past it is rejected with HTTP 507; the
	// store never auto-evicts, so the client frees space with DELETE / DROP TABLE.
	// Distinct from MaxBodyBytes (a per-request limit) — this bounds the whole
	// dataset.
	MaxBytes int64 `json:"max_bytes,omitempty"`
	// contains filtered or unexported fields
}

Handler is the Caddy HTTP handler that embeds hazedb.

func (Handler) CaddyModule

func (Handler) CaddyModule() caddy.ModuleInfo

CaddyModule registers the handler under http.handlers.* so it works as a `handle`/`hazedb` directive or a JSON handler entry.

func (*Handler) Cleanup

func (h *Handler) Cleanup() error

Cleanup deregisters and closes the *DB. DeregisterDBIf is the CAS-safe form: during a config reload the new Provision has already overwritten the slot, so this won't clobber it; it only clears when the handler is fully removed.

func (*Handler) Provision

func (h *Handler) Provision(ctx caddy.Context) error

Provision opens the *DB, runs init_sql, wires the routes, and registers the instance. Called once per module instance at Caddy start / config reload.

func (*Handler) ServeHTTP

func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error

ServeHTTP dispatches to the hazedb mux; unmatched paths fall through to the next handler, so the module can be mounted under a prefix alongside others.

func (*Handler) UnmarshalCaddyfile

func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error

UnmarshalCaddyfile parses the `hazedb` handler directive. Example:

hazedb {
    wal_level       1
    wal_path        /var/lib/hazedb/wal
    wal_rotation    5s
    sqlite_path     /var/lib/hazedb/hazedb.db
    init_sql        /etc/hazedb/schema.sql
    registry_name   default
    max_bytes       1073741824
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL