Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Architecture

This document provides a high-level overview of Martin’s architecture, describing its major components, how they interact, and the rationale behind key design decisions.

Overview

Martin is a blazing fast tile server written in Rust that generates and serves vector tiles on the fly from multiple data sources. It is designed to handle heavy traffic and optimize for speed while maintaining a clean separation of concerns.

graph TB
    Client[Map Client<br/>MapLibre, Leaflet, etc.]

    subgraph Martin["Martin Tile Server"]
        CLI[CLI Entry Point<br/>martin binary]
        Server[HTTP Server<br/>Actix-Web]

        subgraph Sources["Tile Sources"]
            PG[PostgreSQL<br/>Tables & Functions]
            MBT[MBTiles Files]
            PMT[PMTiles Files<br/>Local & Remote]
            COG[Cloud Optimized<br/>GeoTIFF]
        end

        subgraph Resources["Supporting Resources"]
            Sprites[Sprite Generation<br/>SVG to PNG]
            Fonts[Font Glyphs<br/>PBF Format]
            Styles[MapLibre Styles<br/>JSON]
        end

        Catalog[Tile Catalog<br/>Source Registry]
        Cache[Tile/Resources Cache<br/>Moka]
    end

    subgraph Storage["Data Storage"]
        DB[(PostgreSQL<br/>PostGIS)]
        Files[File System<br/>MBTiles/PMTiles]
        S3[Object Storage<br/>S3/Azure/GCP]
    end

    Client -->|HTTP Requests| Server
    Server --> Catalog
    Catalog --> Sources
    Server --> Resources
    Server --> Cache

    PG --> DB
    MBT --> Files
    PMT --> Files
    PMT --> S3
    COG --> Files
    COG --> S3

    Cache -.->|Cached Tiles/Resources| Client

Core Components

Martin’s architecture is organized into four main Rust crates, each with distinct responsibilities:

Purpose: The main tile server binary and HTTP service layer.

Location: /martin

Key Responsibilities:

  • HTTP server using Actix-Web framework
  • Request routing and endpoint handling
  • Configuration parsing (CLI args, env vars, config files)
  • Tile source discovery and initialization
  • Serving the Web UI for tile inspection

Main Modules:

  • src/bin/martin.rs - Server entry point
  • src/bin/martin-cp.rs - Bulk tile copying tool
  • src/srv/ - HTTP service handlers
    • server.rs - Main server setup and routing
    • tiles/ - Tile serving endpoints
    • fonts.rs - Font glyph serving
    • sprites.rs - Sprite serving
    • styles.rs - Style serving
  • src/config/ - Configuration management
    • args/ - CLI argument parsing
    • file/ - Config file parsing
  • martin-ui/ - React-based web interface

Data Flow

sequenceDiagram
    participant Client
    participant Server as HTTP Server
    participant Catalog
    participant Cache
    participant Source as Tile Source
    participant DB as Data Store

    Client->>Server: GET /source_id/z/x/y
    Server->>Catalog: Resolve source_id
    Catalog-->>Server: Source reference
    Server->>Cache: Check cache

    alt Tile in cache
        Cache-->>Server: Cached tile
        Server-->>Client: 200 OK (tile data)
    else Tile not in cache
        Server->>Source: Get tile(z, x, y)
        Source->>DB: Query data
        DB-->>Source: Raw data
        Source->>Source: Generate MVT
        Source-->>Server: Tile data
        Server->>Cache: Store tile
        Server-->>Client: 200 OK (tile data)
    end

Key Design Decisions

Note

🧠 This section provides background and context, not required knowledge. You don’t need to understand or remember all of this to use or contribute to Martin. Read it when you’re curious why certain choices were made.

Rust for Performance and Safety

Why Rust: Martin is written in Rust to balance high performance with strong safety guarantees. (click to expand)
  • Near-C performance without manual memory management
  • Memory safety (no null pointers or buffer overflows)
  • Safe concurrency without data races
  • Zero-cost abstractions that compile to efficient code

Actix-Web Framework

Why Actix-Web: It offers a fast, production-ready async HTTP stack. (click to expand)
  • High-performance async request handling
  • Mature middleware ecosystem
  • Built-in compression and caching headers
  • Easy Prometheus metrics integration

Async/Await Throughout

Why async/await: Allows Martin to handle many concurrent requests efficiently. (click to expand)
  • Handles thousands of concurrent connections
  • Avoids blocking database queries
  • Enables efficient file and network I/O
  • Keeps thread usage low under load

Crate Separation

Why multiple crates: The codebase is split into crates with clear responsibilities. (click to expand)
  • martin-core — reusable core logic and tile sources
  • mbtiles — standalone MBTiles tooling
  • martin — HTTP server, configuration, and runtime wiring
  • martin-tile-utils — shared low-level tile utilities

This makes it easier to:

  • Embed Martin as a library in other Rust projects
  • Use MBTiles tools independently
  • Maintain clear API boundaries and versioning

PostgreSQL Connection Pooling

Why connection pooling: Reuse database connections instead of reconnecting per request. (click to expand)
  • Uses deadpool-postgres
  • Avoids per-request connection overhead
  • Configurable pool sizing
  • Automatic connection health checks

In-Memory Tile Caching

Why caching: Avoid regenerating frequently requested tiles. (click to expand)
  • Fast LRU cache with optional TTLs
  • Automatic eviction of least-used entries
  • Configurable memory limits (default: 512 MB)
  • Thread-safe concurrent access
  • Significant performance improvements for repeated requests

Automatic Source Discovery

Why auto-discovery: Martin tries to work out of the box with minimal configuration. (click to expand)

It can currently automatically detect:

  • PostgreSQL tables with geometry columns
  • PostgreSQL functions that return MVT
  • MBTiles and PMTiles files in configured directories

This keeps common setups close to zero-config.

Multi-Protocol Tile Support

Why multiple formats: Different workloads benefit from different storage models. This lets operators pick the best format for their use case:

Dynamic tiles generated from live data. Best for frequently changing datasets.

On-the-Fly Resource Generation

Why generate resources dynamically: Sprites, fonts, and styles are created on demand. (click to expand)
  • No pre-processing step required
  • Simpler deployments (just provide source files)
  • URL-based customization
  • Less storage overhead

Modular Configuration

Why layered configuration: Martin supports multiple configuration sources. (click to expand)
  • CLI flags for quick testing and overrides
  • Environment variables for containerized deployments
  • Config files for larger or more complex setups
  • Clear precedence between configuration layers

Component Interactions

graph TB
    Martin[Martin Server]
    Pool[Connection Pool<br/>deadpool-postgres]

    subgraph PostgreSQL
        Tables[Tables with<br/>Geometry Columns]
        Functions[MVT Functions]
        PostGIS[PostGIS Extension]
    end

    Martin --> Pool
    Pool --> Tables
    Pool --> Functions
    Tables --> PostGIS
    Functions --> PostGIS

How it works:

  1. Martin connects to PostgreSQL using connection string
  2. Queries geometry_columns view to discover tables
  3. Queries pg_proc to discover MVT-returning functions
  4. Maintains connection pool for efficient query execution
  5. Generates tile SQL queries with bbox parameters
  6. Returns results as MVT tiles

Deployment Patterns

Martin supports multiple deployment patterns:

  • Single binary with embedded WebUI
  • Direct PostgreSQL connection
  • Local file serving
  • Suitable for small to medium deployments

Performance Characteristics

  • Bottleneck: Complex geometry queries on large tables
  • Optimization: Spatial indexes (GIST), connection pooling, query tuning
  • Increase CPU for faster tile generation
  • Increase memory for larger tile cache
  • Faster disk I/O for file sources
  • All tile coordinates validated (z/x/y bounds)
  • SQL injection prevention through parameterized queries
  • Path traversal prevention for file sources
  • URL parsing with strict validation

Extensibility Points

Adding New Tile Sources

To add a new tile source type:

  1. Implement the Source trait in martin-core
  2. Add configuration parsing in martin
  3. Register source in the catalog
  4. Add integration tests

Example source types that could be added:

  • Direct GeoJSON file serving
  • Vector tile rendering from raster data
  • Integration with other spatial databases

Adding New Resource Types

To add new resource endpoints:

  1. Implement resource generator in martin-core
  2. Add HTTP handler in martin/src/srv/
  3. Add configuration support
  4. Update catalog/discovery

Custom Authentication/Authorization

Martin doesn’t include built-in auth, but supports:

  • Reverse proxy authentication (recommended)
  • Custom Actix-Web middleware
  • Token-based access control via proxy

Monitoring and Observability

Martin exposes Prometheus metrics via /metrics:

  • HTTP request counters and histograms
  • Tile generation time histograms
  • Cache hit/miss rates
  • Database connection pool stats
  • Error rates by type