moonbitstack/moonp8s/multiproc does not have a README file

    Entry

    pub(all) struct Entry {
    metric : String
    sample : String
    labels : Array[(String, String)]
    help : String
    value : Double
    timestamp : Double
    }

    One value in one worker's file: the family it belongs to, the name the sample is written under, its labels, the family's help text, the value, and the time the value was recorded (zero when it was not recorded with one).

    The pair of metric and sample is what lets a counter be filed under orders and written as orders_total, and a histogram bucket under orders_seconds and orders_seconds_bucket.

    Entry::new

    fn Entry::new(metric : String, sample : String, labels : Array[(String, String)], help : String, value : Double, timestamp? : Double) -> Entry

    One value for a worker's file.

    Mode

    pub(all) enum Mode {
    All
    LiveAll
    Min
    LiveMin
    Max
    LiveMax
    Sum
    LiveSum
    MostRecent
    LiveMostRecent
    }

    How the values of one gauge from several processes become one series.

    A gauge has no natural merge — a counter can be added up, a bucket can be added up, but "requests in flight" from three workers is a policy decision. All and LiveAll keep the processes apart, one series each, labelled pid; the other four collapse them: the smallest, the largest, the sum, or the value set most recently.

    The Live spellings mean the same thing at a scrape. They decide what happens when a process exits: mark_dead removes the files of a live mode, and leaves the others, which is what makes a handful of gauges forget a dead worker instead of keeping its last value forever.

    Mode::code

    fn Mode::code(self : Mode) -> String

    The spelling a file name carries: all, liveall, min, livemin, max, livemax, sum, livesum, mostrecent, livemostrecent.

    Mode::is_live

    fn Mode::is_live(self : Mode) -> Bool

    Whether this mode forgets a process when it exits. mark_dead removes the files of these modes only.

    Mode::is_most_recent

    fn Mode::is_most_recent(self : Mode) -> Bool

    Whether this mode keeps the value set most recently instead of combining values, which is why a most-recent gauge cannot be stepped with inc.

    Mode::is_separate

    fn Mode::is_separate(self : Mode) -> Bool

    Whether this mode keeps the processes apart, one series per pid.

    Mode::of

    The mode a file name spells. An unknown spelling is refused: it would change what the numbers mean, which is worse than a failed scrape.

    Part

    pub(all) struct Part {
    pid : String
    kind :
    Kind

    mode : Mode?
    entries : Array[Entry]
    }

    One worker's file, decoded: whose it is, what kind of metric it holds, the gauge mode it was written for, and its values.

    Part::decode

    fn Part::decode(file_name : String, bytes : Bytes) -> Part raise
    Refused

    Decode one worker's file. A file shorter than its header claims is refused rather than half-read: a torn file means a writer that does not write whole files, and guessing at the remainder would report numbers nobody wrote.

    Part::encode

    fn Part::encode(self : Part) -> Bytes

    The bytes of this part: a four-byte used count, four bytes of padding, then one record per entry — a key length, the key, padding to an eight-byte boundary, the value, and the timestamp, each value a little-endian double.

    This is the layout the official clients' multiprocess mode uses, which is the point: a directory of these files is readable by them and by this library alike.

    Part::file_name

    fn Part::file_name(self : Part) -> String

    The name this part is stored under: <kind>_<pid>.db, or gauge_<mode>_<pid>.db for gauges, which is the layout the official clients read and write.

    Part::name_of

    The part a file name announces. A gauge file carries its mode in the name because the mode is a property of how the worker wrote the value, and the scrape has no other way to learn it.

    Part::new

    fn Part::new(pid : String, kind :
    Kind
    , mode : Mode?, entries : Array[Entry]) -> Part

    One worker's file.

    merge

    Fold the parts into one set of families.

    Counters and summaries add up across processes; histograms add up per bucket and are made cumulative again; a gauge follows its mode. Families come out in the order their first part mentions them, and series in the order they were first seen, so two runs over the same directory write the same bytes.

    A family that two files disagree about — a name that is a counter in one process and a histogram in another, or a gauge written under two modes — is refused. The numbers would be meaningless, and a scrape that says so is better than one that quietly picks a side.