TurboAPI

repository·main·Indexed 21 days ago

https://github.com/justrach/turboapi

A high-performance, FastAPI-compatible Python web framework featuring a Zig-native HTTP core. Designed for extreme throughput in HTTP-only and uncached HTTP+DB workloads, it leverages Zig for routing, validation, and response handling, offering significant performance gains with Python 3.14 free-threading. The ecosystem includes TurboPG for high-speed PostgreSQL operations and faster-boto3 for accelerating boto3 internals.

Tokens
37.6K
Snippets
142
Records
184
Agent score
76%

What's inside turboapi

  1. Understand the TurboAPI component layers

    main

    TurboAPI is composed of three primary layers:

    1. Python Layer (python/turboapi/): Provides a FastAPI-compatible interface. It includes TurboAPI (base class), ZigIntegratedTurboAPI (handles Zig integration and handler classification), and NativeIntegratedTurboAPI.
    2. Zig Core (zig/src/): The high-performance engine. It includes the TCP server, a Radix trie router, the Python C-API bridge (py.zig), and the dhi_validator.zig for pre-GIL validation.
    3. Build System (zig/build.zig): Manages the compilation of the C extension, linking against Python and dhi libraries.
  2. Performance benefits of faster-boto3

    main

    By replacing urllib3 with a Zig HTTP client and utilizing SIMD parsers, faster-boto3 provides significant performance improvements over vanilla boto3. Key optimizations include:

    • XML Tag Extraction: 44x faster (improves S3 ListObjects).
    • Timestamp Parsing: 368x faster (via NEON vectorization).
    • SigV4 Signing: 7x faster (via HMAC-SHA256 chain).
    • SHA256 Hashing: Hardware accelerated.

    When used in a full stack with TurboAPI, throughput can increase significantly (e.g., from ~1,470 req/s to ~170,000 req/s for S3 GetObject).

  3. Understand the capabilities of turboapi-core

    main

    turboapi-core is a high-performance Zig library designed for URL routing and HTTP parsing. It is not a standalone HTTP server or a web framework, but a set of primitives that can be integrated into them.

    Key features include:

    • High-Performance Routing: Uses a prefix-compressed radix trie (similar to Go's httprouter) with method-indexed trees (separate tries for GET, POST, etc.).
    • Path Matching: Supports {param} extraction, *wildcard matching, and automatic path traversal rejection.
    • HTTP Utilities: Includes percentDecode, queryStringGet, and statusText.
    • Performance: Capable of ~43.5M lookups/sec (approx. 23ns per match) for fixed routes, and ~28.6M lookups/sec for runtime paths with varying parameters.
    • Zero Dependencies: Lightweight implementation (approx. 530 lines of Zig).
  4. What is turboapi-core and its architecture?

    main

    Concept

    turboapi-core is a shared Zig HTTP core extracted from the turboAPI Python framework. It serves as a common foundation for both turboAPI (a Python web framework) and merjs (a Zig-native full-stack framework).

    Architecture Model

    Instead of sharing the entire server stack, turboapi-core provides the logic layer while allowing frameworks to implement their own transport/TCP layers:

    • Shared Layer: Routing logic, HTTP parsing utilities, and caching primitives. This ensures that bug fixes or performance improvements in the core benefit all consuming frameworks simultaneously.
    • Framework-Specific Layer:
      • turboAPI implements a custom TCP/server layer to manage Python thread states (PyThreadState).
      • merjs implements a layer using Zig's std.http.Server and std.Thread.Pool.

    This separation prevents leaky abstractions between the Python runtime and the Zig runtime while maintaining high performance (e.g., 134k req/s with 0.16ms avg latency).

  5. How TurboAPI architecture and request lifecycle works

    main

    TurboAPI uses a hybrid architecture where a high-performance Zig HTTP core handles the heavy lifting (TCP, header parsing, routing, and validation), while Python is reserved strictly for business logic.

    Key Architectural Concepts:

    • Shared Core: Uses turboapi-core (a Zig library) for radix trie routing and HTTP utilities.
    • Zig-side Validation: For routes using dhi models, JSON is parsed and validated in Zig. If validation fails, a 422 error is returned immediately without ever acquiring the Python GIL or calling your handler.
    • Zero-Copy Responses: On the response path, Zig accesses Python string buffers directly via PyUnicode_AsUTF8() to write to the socket, avoiding memcpy and extra heap allocations.
    • Handler Classification: The server analyzes routes at startup to assign the most efficient dispatch path (e.g., native_ffi for C/Zig handlers, model_sync for dhi models, or simple_sync for standard handlers).
  6. Compare TurboAPI vs FastAPI (End-to-End HTTP)

    main

    If you need to measure performance including the HTTP layer (e.g., comparing TurboAPI against FastAPI), do not use the pgbench directory. Instead, use the benchmarks/postgres directory.

    The pgbench suite is strictly for driver-to-database performance, whereas the benchmarks/postgres suite measures the full HTTP + DB stack.

  7. Understand benchmark metrics and results

    main

    When interpreting TurboAPI benchmark results, focus on these key metrics:

    • Sequential Latency: The time taken for single requests processed one at a time.
    • Concurrent Latency: The average time taken for requests under parallel load.
    • Throughput (RPS): The maximum sustainable Requests Per Second.
    • P95/P99: The 95th and 99th percentile latency, representing tail latency.

    Performance Observations

    • I/O Bound Tasks: Async handlers typically outperform sync handlers when dealing with I/O wait (e.g., 1ms wait).
    • CPU Bound Tasks: Sync handlers may be slightly faster for purely computational tasks or low-concurrency sequential requests.
  8. Understand the pgbench driver comparison model

    main

    The pgbench suite measures raw driver throughput (queries per second and latency percentiles) by communicating directly with PostgreSQL via the binary wire protocol. It does not include HTTP overhead.

    Driver Comparison Matrix

    DriverRuntimeConcurrency model
    asyncpgPython 3.11 + uvloopasyncio (single-threaded)
    psycopg3-asyncPython 3.11 + asyncioasyncio (single-threaded)
    turbopg (pg.zig)Python 3.14t + ZigThreadPoolExecutor (GIL released)
  9. How handler classification optimizes performance

    main

    To avoid expensive runtime introspection (like inspect.iscoroutinefunction) during every request, TurboAPI classifies handlers at startup.

    When you register a route via add_api_route(), the classify_handler() function analyzes the function's signature and annotations. It determines one of six specific handler types, allowing the Zig core to use direct dispatch instead of generic wrappers. This ensures that simple GET requests or standard Pydantic model POSTs follow the fastest possible execution path in the Zig-to-Python bridge.

  10. Select the correct handler type (Sync vs Async)

    main

    Choosing the right handler type is critical for performance. Use sync handlers for pure CPU computation to avoid async overhead, and async handlers for I/O-bound tasks (like database queries or external API calls) to prevent blocking the event loop.

    Handler Selection Matrix

    ScenarioHandler TypeWhy
    Simple GETsimple_syncLowest overhead
    GET with databasesimple_asyncNon-blocking I/O
    POST with validationmodel_syncSIMD JSON + dhi
    POST with external APIbody_asyncNon-blocking I/O
    Complex dependenciesenhancedFull wrapper needed
    # Faster for pure computation
    @app.get("/compute")
    def compute():
        return {"result": sum(range(1000))}
    
    # Use async only for I/O
    @app.get("/fetch")
    async def fetch():
        return await database.query(...)
  11. Understand the TurboAPI vs FastAPI benchmark methodology

    main

    The benchmark suite compares three specific stacks across identical HTTP routes to measure end-to-end performance:

    Stacks Compared:

    1. TurboAPI + pg.zig/turbopg
    2. FastAPI + asyncpg
    3. FastAPI + SQLAlchemy

    Tested Routes:

    • GET /health (No-DB route)
    • GET /users/{id} (Varying IDs)
    • GET /users?age_min=20 (Filtered query)
    • GET /search?q=user_42% (Search query)

    Environment Details:

    • Database: Postgres 18 in Docker.
    • Load Generation: wrk.
    • Goal: To measure full web stack speed and the overhead added by frameworks like FastAPI on top of database clients.
  12. Use vectored metrics with labels

    main

    Vectored metrics (e.g., CounterVec, GaugeVec, HistogramVec) allow attaching strongly-typed labels to metrics.

    Requirements:

    • They require an std.mem.Allocator during initialization.
    • Initialization can fail (!void).
    • Labels are compile-time checked using a struct.
    • Supported label types: ErrorSet, Enum, Type, Bool, Int, and []const u8.

    Usage Pattern: Define the label struct within your Metrics definition, initialize with an allocator, and pass the label struct to the metric methods (like incr or observe).

    const m = @import("metrics");
    const Allocator = @import("std").mem.Allocator;
    
    const Metrics = struct {
        hits: Hits,
        const Hits = m.CounterVec(u32, struct { status: u16, name: []const u8 });
    };
    
    var metrics = m.initializeNoop(Metrics);
    
    pub fn initializeMetrics(allocator: Allocator, opts: m.RegistryOpts) !void {
        metrics = .{ 
            .hits = try Metrics.Hits.init(allocator, "hits", .{}, opts) 
        };
    }
    
    pub fn hit(labels: struct { status: u16, name: []const u8 }) !void {
        try metrics.hits.incr(labels);
    }
    
    // Usage:
    // try hit(.{ .status = 200, .name = "/api" });