Trickster Documentation

repository·main·Indexed 24 days ago

https://github.com/trickstercache/trickster

Trickster is an HTTP reverse proxy/cache and time series database (TSDB) accelerator designed to improve dashboard performance and reduce redundant queries to backends such as Prometheus, InfluxDB, and ClickHouse.

Tokens
40.2K
Snippets
85
Records
204
Agent score
84%

What's inside Trickster

  1. What is Trickster?

    main

    Trickster is a dual-purpose tool that functions as both an HTTP reverse proxy/cache and a dashboard query accelerator for time series databases (TSDBs).

    As an HTTP Reverse Proxy Cache, it serves as a high-performance layer for static file servers and web APIs, offering features like TLS termination, multiple caching backends (Redis, bbolt, in-memory, filesystem), and distributed tracing via OpenTelemetry.

    As a Time Series Database Accelerator, it sits in front of TSDBs to improve dashboard rendering times by eliminating redundant computations. It achieves this through Delta Proxy Caching, Step Boundary Normalization, and a 'Fast Forward' feature for real-time data.

  2. What is Collapsed Forwarding in Trickster

    main

    Collapsed Forwarding is a feature used to prevent multiple simultaneous requests for the same object from overwhelming an upstream origin during a cache miss or revalidation attempt. Instead of every user triggering a new request to the origin, Trickster ensures only a single request is performed for a specific object.

    Trickster supports two modes:

    1. Basic Collapsed Forwarding (Default): When a cache miss occurs, all subsequent requests for that same object are placed on a 'waitlist'. Once the first request successfully populates the cache, all waitlisted requests are served from the newly cached object.
    2. Progressive Collapsed Forwarding (PCF): An improvement that eliminates the waitlist. It serves simultaneous requests concurrently while the object is still being downloaded from the origin (similar to 'read-while-write'). This minimizes Time to First Byte (TTFB) for popular objects, which is particularly beneficial for low-latency streaming like DASH or HLS video delivery.
  3. What is Negative Caching in Trickster

    main

    Negative Caching allows Trickster to cache undesired HTTP responses (status codes $\ge$ 300 and $<$ 600) for a short period. This prevents overwhelming backend systems during 'thundering herd' scenarios (e.g., many requests for a non-existent object) or during service downtime.

    Key behaviors:

    • TTL Override: For any code in the Negative Cache Map, the response's effective TTL is overridden by the configured Negative Cache TTL, ignoring backend headers.
    • Transparency: All original response headers remain unmodified. Clients can detect a negative cache hit via the X-Trickster-Result header, which will contain the status nchit.
    • Scope: Negative caching only applies to Cacheable Objects; it does not apply to Proxy-Only configurations.
    • Limitations: The Negative Cache Map must be an explicit list of status codes. Wildcards or ranges are not supported.
  4. Configure a Rule

    main

    A Rule consists of required parts to define what to inspect and where to go, and optional parts to refine the inspection.

    Required Parts

    • input_source: The part of the Request to inspect.
    • input_type: The data type of the source.
    • operation: The logic applied to the input.
    • next_route: The Backend Name for the default route if no cases match. (Alternatively, use redirect_url to issue a 302).
    • redirect_url: A fully-qualified URL for a 302 redirect in the default case. (Alternatively, use next_route).

    Optional Parts

    • input_key: Case-sensitive lookup key (required for header or param sources).
    • input_encoding: Encoding to decode before the operation (e.g., base64).
    • input_index: If > -1, the source is split and the value is extracted from this index.
    • input-delimiter: The delimiter used for splitting when input_index is used (defaults to ' ').
    • ingress_req_rewriter: Name of a rewriter to run before rule execution.
    • egress_req_rewriter: Name of a rewriter to run after rule execution.
    • nomatch_req_rewriter: Name of a rewriter to run after execution if no cases matched.
    • max_rule_executions: Limit on rule chaining depth (default: 16). If exceeded, returns a 400 status code.
  5. How Byterange Chunking works

    main

    Byterange chunking splits data into pieces of a fixed literal size.

    The process follows these steps:

    1. Determine Chunk Size: Uses the byterange_chunk_size (default 4096).
    2. Determine Range: Uses the range or content length from the cache read/write request.
      • Note: If a range cannot be determined on a write, an error occurs. If it cannot be determined on a read, it reads until the query fails.
    3. Alignment: Determines a maximum range aligned to the chunk size that contains the entire byterange.
    4. Write: Writes each chunk-sized range using RangeParts (where provided ranges are cropped to the chunk range) under a subkey.
    5. Read: Reads the subkeys and reconstitutes the body from RangeParts.
  6. Configure CORS modes in Trickster

    main

    Trickster supports four CORS modes to control how Access-Control-* response headers are handled. When using preserve or merge modes, Trickster automatically includes the request's Origin header in the cache key to ensure origin-specific responses are not served to the wrong origin.

    ModeBehavior
    preserveReturns the origin's Access-Control-* response headers unchanged.
    mergePreserves origin CORS headers, then applies configured headers as overrides.
    replaceRemoves all origin Access-Control-* response headers, then applies the configured headers.
    disableRemoves all origin Access-Control-* response headers. This mode cannot include a headers block.

    Note on Legacy Behavior: If no cors block is provided for a backend or path, Trickster defaults to setting Access-Control-Allow-Origin to * while leaving other CORS headers unchanged.

  7. Configure Path Matching Scope

    main

    You can control how a path configuration matches incoming client requests using the match_type setting.

    • exact (default): The client's requested URL path must be an exact match. For example, /foo will not match a request to /foo/bar.
    • prefix: Matches any client-requested path with the longest prefix match. For example, a prefix match for /foo will match /foo/bar, /foobar, and /food.

    Tip: To avoid unintentional matches with prefix, consider ending your path configuration with a trailing slash (e.g., /foo/).

  8. Configure Inbound Listeners

    main

    The listeners map defines how Trickster accepts incoming connections. Trickster automatically defines three default entries: default, metrics, and mgmt.

    Backends are mapped to listeners using the listener_name field. If listener_name is omitted in a backend configuration, it defaults to the default listener.

    Important Constraints:

    • Backends cannot use the reserved mgmt or metrics listeners.
    • A user-defined listener with no mapped backend will not start and will trigger a warning.
    • A TLS port is only enabled if at least one backend mapped to that listener provides a valid frontend certificate and key in its tls section.
    • If a listener's network settings (address/port) change during a reload, Trickster drains the old listener before starting the new one (default drain period is 30 seconds).
    listeners:
      default:
        address: ""
        port: 8480
        tls_address: ""
        tls_port: 8483
        connections_limit: 0
        read_header_timeout: 10s
      private_api:
        protocol: http
        address: 127.0.0.1
        port: 9080
    
    backends:
      default:
        listener_name: default
        provider: prometheus
        origin_url: http://prometheus:9090
      private:
        listener_name: private_api
        provider: reverseproxy
        origin_url: http://private-origin
  9. How Timeseries Request Sharding works in Trickster

    main

    Trickster 2.0 supports sharding upstream HTTP requests when retrieving timeseries data. When a request is eligible for sharding, Trickster inspects the requested time range and subdivides it into smaller ranges based on your backend configuration. These sharded requests are sent to the origin concurrently. Once all responses are returned, Trickster reconstitutes them into a single dataset for the user.

    This mechanism is used to prevent overwhelming upstream backends with massive time range queries by breaking them into manageable chunks.

  10. How Time Series Merge (TSM) works

    main

    The Time Series Merge mechanism is used for High Availability (HA) or Federation of time series data.

    High Availability (HA)

    Set the replica_group option to the same value on physical backends that are HA replicas of the same logical shard. TSM will coalesce these replicas, using the configured pool order to resolve overlapping points and using later replicas to fill gaps.

    Federation

    For non-overlapping data, Trickster automatically selects a merge strategy per query by inspecting the outermost PromQL aggregation operator (e.g., sum, avg, max).

    Key Rules:

    • Replica Grouping: If replica_group is omitted, it defaults to the backend name. Explicitly set it for HA pools using non-idempotent aggregations like sum, count, or avg to prevent double-counting.
    • Boundary: When a TSM pool member is itself an ALB, set replica_group on that nested ALB. Trickster uses the wrapper as the replica-group boundary.
    • Fallback: For request paths that are not mergeable, TSM dispatches the request directly to the first live pool target.
    • Injected Labels: Labels used for routing/metadata are automatically stripped before merging to ensure series from different backends hash identically.
    backends:
      prom01a:
        provider: prometheus
        replica_group: prom01
        origin_url: http://prom01a.example.com:9090
    
      prom01b:
        provider: prometheus
        replica_group: prom01
        origin_url: http://prom01b.example.com:9090
    
      prom-alb-01:
        provider: alb
        alb:
          mechanism: tsm
          pool:
            - prom01a
            - prom01b
  11. Understand the difference between instant-query and query-range tests

    main

    Trickster provides two primary load test scripts with different behaviors:

    1. instant-query.js: Tests the /api/v1/query endpoint. It passes if 95% of requests complete under 500 ms and >99% return a successful response.
    2. query-range.js: Simulates a Grafana dashboard. Each VU iteration performs query_range requests for all queries in PANEL_QUERIES using a floating time window (defaulting to the last 6 hours). The start/end timestamps advance with wall-clock time, and the VU sleeps for the duration of REFRESH_INTERVAL between iterations.
  12. How Trickster accelerates ClickHouse queries

    main

    Trickster accelerates ClickHouse queries that return time series data (typically used in dashboards) by using the Time Series Delta Proxy Cache. This mechanism minimizes the number and time range of queries sent to the upstream ClickHouse server by caching time series results and normalizing time ranges.

    Supported Clients

    • Grafana: Tested with the ClickHouse DataSource Plugin for Grafana (v1.9.3) using the $timeSeries macro.
    • Go SDK: Supports clickhouse-go/v2.
      • HTTP Protocol: Queries made via clickhouse.OpenDB are proxied and cached.
      • Native Binary Protocol: Supported via transparent proxying (OPC).

    Cacheable Query Requirements

    To be cacheable as a time series, the first column expression in the main or any subquery must follow one of these two formats to allow Trickster to derive the timestamp column and the step size:

    #### Grafana Plugin Format 
    ```sql
    SELECT intDiv(toUInt32(time_col, 60) * 60) [* 1000] [as] [alias]

    ClickHouse Time Grouping Function

    SELECT toStartOf[Period](time_col) [as] [alias]