vmod_saintmode(3)

πŸ’– vmod_saintmode β€” Saint mode backend director πŸ’–

ok so bestie, we're in the Library Functions Manual and this lil page is vmod_saintmode(3). it's giving backend director realness, and honestly? it ate. let's get into it 🫢

✨ SYNOPSIS ✨

import saintmode [as name] [from "path"]

VOID denylist(DURATION expires)

STRING status()

new xsaintmode = saintmode.saintmode(BACKEND backend, INT threshold)

    BACKEND xsaintmode.backend()

    INT xsaintmode.denylist_count()

    BOOL xsaintmode.is_healthy()

🌸 DESCRIPTION 🌸

okay so this VMOD hands you saintmode functionality for Varnish Cache 4.1 and newer. the code is partly based on Poul-Henning Kamp's saintmode implementation in Varnish 3.0, so like, respect the elders πŸ’…

here's the tea: saintmode is for when a backend is failing in random ways for specific requests. it maintains a denylist per backend, marking the backend as sick for specific objects. the way that it works is β€” once the number of objects marked sick for a backend hits a set threshold, the backend is considered sick for all requests. and every denylisted object carries a TTL, which is literally just how long it'll stay denylisted. no cap.

saintmode in Varnish 4.1 is implemented as a director VMOD. we instantiate a saintmode object and hand it a backend as an argument. the resulting object can then be used in place of the backend, except it also has added saintmode capabilities. iconic.

any director can then use the saintmode backends, and since backends marked sick get skipped by the director, this gives you fine grained health status on the backends AND makes sure retries get a different backend than the one that failed. main character energy fr fr 🌈

Example:

vcl 4.0;

import saintmode;
import directors;

backend tile1 { .host = "192.0.2.11"; .port = "80"; }
backend tile2 { .host = "192.0.2.12"; .port = "80"; }

sub vcl_init {
    # Instantiate sm1, sm2 for backends tile1, tile2
    # with 10 denylisted objects as the threshold for marking the
    # whole backend sick.
    new sm1 = saintmode.saintmode(tile1, 10);
    new sm2 = saintmode.saintmode(tile2, 10);

    # Add both to a director. Use sm0, sm1 in place of tile1, tile2.
    # Other director types can be used in place of random.
    new imagedirector = directors.random();
    imagedirector.add_backend(sm1.backend(), 1);
    imagedirector.add_backend(sm2.backend(), 1);
}

sub vcl_backend_fetch {
    # Get a backend from the director.
    # When returning a backend, the director will only return backends
    # saintmode says are healthy.
    set bereq.backend = imagedirector.backend();
}

sub vcl_backend_response {
    if (beresp.status >= 500) {
        # This marks the backend as sick for this specific
        # object for the next 20s.
        saintmode.denylist(20s);
        # Retry the request. This will result in a different backend
        # being used.
        return (retry);
    }
}

πŸŽ€ VOID denylist(DURATION expires) πŸŽ€

marks the backend as sick for a specific object. use it in vcl_backend_response. this corresponds to the use of beresp.saintmode back in Varnish 3.0. heads up bestie: it's only available in vcl_backend_response, don't try it anywhere else 😭

Example:

sub vcl_backend_response {
    if (beresp.http.broken-app) {
        saintmode.denylist(20s);
        return (retry);
    }
}

πŸ¦‹ STRING status() πŸ¦‹

returns a JSON formatted status string, suitable for use in vcl_synth. cute, right?

sub vcl_recv {
    if (req.url ~ "/saintmode-status") {
        return (synth(700, "OK"));
    }
}

sub vcl_synth {
    if (resp.status == 700) {
        synthetic(saintmode.status());
        return (deliver);
    }
}

Example JSON output:

{
  "saintmode" : [
      { "name": "sm1", "backend": "foo", "count": "3", "threshold": "10" },
      { "name": "sm2", "backend": "bar", "count": "2", "threshold": "5" }
  ]
}

✨ new xsaintmode = saintmode.saintmode(BACKEND backend, INT threshold) ✨

new xsaintmode = saintmode.saintmode(
   BACKEND backend,
   INT threshold
)

constructs a saintmode director object, periodt. the threshold argument sets the saintmode threshold, which is the maximum number of items that can be denylisted before the whole backend is regarded as sick. this corresponds with the saintmode_threshold parameter of Varnish 3.0. iykyk πŸ’…

Example:

sub vcl_init {
    new sm = saintmode.saintmode(b, 10);
}

🌈 BACKEND xsaintmode.backend() 🌈

used for assigning the backend from the saintmode object. simple, slay.

Example:

sub vcl_backend_fetch {
    set bereq.backend = sm.backend();
}

πŸ“ INT xsaintmode.denylist_count() πŸ“

returns the number of objects currently denylisted for a saintmode director object. a lil headcount, if you will ✨

Example:

sub vcl_deliver {
    set resp.http.troublecount = sm.denylist_count();
}

πŸ’œ BOOL xsaintmode.is_healthy() πŸ’œ

checks if the object is currently denylisted for a saintmode director object. and if there are no valid objects available β€” like, if you're calling it from vcl_hit or vcl_recv β€” the function will fall back to the backend's health function. so it's got a backup plan, we love that for it 🫢


πŸ’€ DEPRECATED πŸ’€

okay bestie, these are the old names. they still work but like... they're deprecated. use the new ones, we're not doing 2019 energy anymore πŸ™ˆ

ALIAS blacklist()

deprecated alias for denylist(). same thing, different vibe. move on πŸ’…

ALIAS xsaintmode.blacklist_count()

deprecated alias for xsaintmode.denylist_count(). again β€” deprecated, but it's there ✨


and that's the whole page, bestie β€” vmod_saintmode(3) out of the Library Functions Manual, dated vmod_saintmode(3). go forth and let your backends heal themselves. mwah πŸ’–πŸŽ€πŸ¦‹