GDNSD-PLUGIN-WEIGHTED(8)

✨ NAME ✨

gdnsd-plugin-weighted β€” the gdnsd plugin that does "weighted" records πŸ’…

It's literally the manual page for it, bestie. The full title is GDNSD-PLUGIN-WEIGHTED(8), from the gdnsd section of the manual. πŸŽ€


🌸 SYNOPSIS 🌸

okay so here's an example plugin config, no cap:

plugins => {
  weighted => {
    multi = false # default
    service_types = up
    up_thresh => 0.5 # default
    corpwww => {
      lb01 = [ lb01.example.org., 99 ]
      lb02 = [ lb02.example.org., 15 ]
      lb03 = [ lb03.example.org., 1 ]
    }
    frontwww6 => {
      service_types = up
      multi = true
      wwwhost01 = [ 2001:db8::123, 4 ]
      wwwhost02 = [ 2001:db8::456, 1 ]
      wwwhost03 = [ 2001:db8::789, 2 ]
    }
    pubwww => {
      service_types = [ web_check, foo ]
      up_thresh => 0.01,
      pubhost01 = [ 192.0.2.1, 44 ]
      pubhost02 = [ 192.0.2.2, 11 ]
      pubhost03 = [ 192.0.2.3, 11 ]
      pubhost04 = [ 192.0.2.4, 11 ]
    }
    cdnwww => {
      service_types = web_check
      datacenter1 => {
        d1-lb1 = [ 127.0.0.1, 2 ]
        d1-lb2 = [ 127.0.0.2, 2 ]
      }
      datacenter2 => {
        d2-lb1 = [ 127.0.0.3, 2 ]
        d2-lb2 = [ 127.0.0.4, 2 ]
        d2-lb3 = [ 127.0.0.5, 1 ]
      }
    }
    mixed => {
      multi => false,
      addrs_v4 => {
        lb1 = [ 127.0.0.3, 2 ]
        lb2 = [ 127.0.0.4, 2 ]
      }
      addrs_v6 => {
        multi => true
        www6set1 = {
          lb01 => [ 2001:db8::123, 4 ]
          lb02 => [ 2001:db8::456, 1 ]
        }
        www6set2 => {
          lb01 => [ 2001:db8::789, 4 ]
          lb02 => [ 2001:db8::ABC, 1 ]
        }
      }
    }
    cn => {
      service_types = my_cn_check
      foo = [ lb01.example.org., 99 ]
      bar = [ lb02.example.org., 15 ]
    }
  }
}

and here's the zonefile RRs referencing all that ^^ , in example.com:

www.corp   300 DYNC weighted!corpwww
www6.front 300 DYNA weighted!frontwww6
www        300 DYNC weighted!pubwww
cdn        300 DYNA weighted!cdnwww
mixed-a    300 DYNA weighted!mixed
cnames     300 DYNC weighted!cn

πŸ¦‹ DESCRIPTION πŸ¦‹

gdnsd-plugin-weighted is the plugin you use when you want to hand back one (or a lil subset πŸ‘€) of several address records, or one of several CNAME records β€” and you want the choice to be based on dynamic-weighted probabilities. slay. ✨


πŸ’– CONFIGURATION - TOP LEVEL πŸ’–

Up at the top level, there are three special parameter keys: service_types, up_thresh, and multi. Heads up: multi is ignored for CNAME-based resources. And all three of these keys are inherited and override-able down at the per-resource and per-address-family levels. so they trickle down, fr fr. 🫢

  • service_types β€” this sets how the applicable addresses or CNAMEs get monitored. The top-level default for service_types is up, which is a built-in service type that gdnsd provides for you. Wanna know about configuring non-default service types? Go read the main gdnsd.config(5) documentation. πŸ“–
  • multi β€” a boolean, so it can be true or false, and it defaults to false. multi controls how the algorithm behaves when it's picking result addresses. We get into the gritty details later, promise. πŸ’…
  • up_thresh β€” defines a floating point fraction of summed address weights, in the range (0.0 - 1.0], and it defaults to 0.5. It's the thing that influences failure/failover behavior. ⚠️

Other than those three lil guys, the rest of the top level keys are just the names of your resources, and their values are the configuration of each resource. iconic.


πŸŽ€ CONFIGURATION - PER-RESOURCE πŸŽ€

Inside a given resource's configuration hash, you can once again specify those three address-related parameters β€” services_types, multi, and up_thresh β€” to override their settings for that specific resource. (yes, the original really does say services_types there, we don't make the rules πŸ™ˆ)

There are two basic configuration modes within a resource:

  1. Explicit per-family address sub-stanzas. In this mode, the resource contains one or more of the keys addrs_v4 and addrs_v6. Usually you'd use both together, but it's simpler to use the second option when you're configuring just a single address family. The contents of each stanza configure response RRs of the given address type for this resource, and the 3 behavioral parameters service_types, multi, and up_thresh can be overridden per-address-family too. 🌈
  2. Automatic top-level detection of just one address family or CNAMEs. In this mode you can configure the top-level of a resource with direct entries β€” as long as they're a matching set of a single type: all IPv4 addresses, all IPv6 addresses, or all CNAMEs. The type gets auto-detected. ✨

Resources that contain weighted lists of CNAMEs rather than addresses can only be used with DYNC RRs in zonefiles. Resources that contain only addresses can be used in either DYNC or DYNA RRs. πŸ’«


🧚 CONFIGURATION - CNAMES 🧚

When you're configuring cnames, the value of each item should be [ CNAME, WEIGHT ], and the resource will be useful for DYNC zonefile records β€” resolving to a weighted CNAME record in responses. 🎯

The selection algorithm, based on weights and monitoring results, is documented below for addresses in THE UNGROUPED SINGLE CASE β€” because groups of CNAMEs cannot be configured, and the multi option is not valid for them.

CNAME data must be fully qualified (it ends in .), and a DYNC cannot be used to emit a CNAME that points into the same zone that contained the DYNC. 😭

πŸ‘‘ CONFIGURATION - ADDRESSES πŸ‘‘

With the one exception that addrs_v4 and addrs_v6 must contain only addresses of the correct family (or, in the top-level auto-detect case, the top level entries must all be of the same family), the two stanzas behave identically. And when both are present, they're both used in every DYNA response β€” because gdnsd always includes opposite-family records in the Additional section of A/AAAA queries. πŸ’…

Within either address family type, there are two different binary dimensions (multi -> true/false, and grouped-vs-ungrouped) that the configuration and behavior hinge on, which gives you four different possible cases:

  • ungrouped-single
  • ungrouped-multi
  • grouped-single
  • grouped-multi

Each one gets its own detailed moment below. ⭐


🌸 THE UNGROUPED SINGLE CASE 🌸

This is the simplest case, bestie. The code detects it when it sees that multi is false (that's the default), and that the values of the keys are arrays rather than sub-hashes. Each hash key is an address label, and each value is an array of [ IPADDR, WEIGHT ].

When answering a query in this case, first the weights get converted to dynamic weights. The dynamic weight of an address is its configured weight if the monitored state is UP, or zero if the monitored state is DOWN. πŸ”₯

Then the dynamic weights are summed to produce a dynamic weight total, and a single address to respond with is chosen from the set β€” with each address having the odds addr_dynamic_weight / total_dynamic_weight.

However β€” plot twist β€” if the total_dynamic_weight is less than ceil(up_thresh * total_configured_weight), then the dynamic weights are all reset to their configured full values, so the response odds are the same as if everyone were UP. And when that happens, resource-level failure is signalled to any upper-layer meta-plugin (e.g. metafo or geoip) when applicable. 😱

Example (where X could be a whole resource, or an addrs_v4 stanza):

X => {
  multi => false # default
  # odds below assume no addresses are down:
  lb01 => [ 192.0.2.1, 45 ] # 25% chance (45/180)
  lb02 => [ 192.0.2.1, 60 ] # 33% chance (60/180)
  lb03 => [ 192.0.2.1, 75 ] # 42% chance (75/180)
}

πŸ’œ THE UNGROUPED MULTI CASE πŸ’œ

This case gets detected when (like above) the values of the keys are arrays of [ IPADDR, WEIGHT], but the parameter multi is true. 🌈

The change from the above behavior is primarily that multiple addresses from the weighted set can be returned in each response. The maximum β€” rather than the sum β€” of the dynamic weights (again, zero for down addresses, configured-weight otherwise) is found, and the odds of each address's inclusion in the response set is addr_dyanmic_weight / max_dynamic_weight.

This means that all non-DOWN addresses which share the group's maximum dynamic weight value will always be included β€” while the others get optionally included depending on the odds. 🎲

At least one address is always returned (because logically, at least one address has the maximum weight, giving it a 100% chance), and sometimes the full non-DOWN set will be returned. ✨

up_thresh behaves exactly as in the previous case: if the sum of the dynamic weight values is less than ceil(up_thresh * total_configured_weight), then the dynamic weights are all set to their configured values and the result set is calculated as if everyone were UP β€” all while signalling resource-level failure to upstream meta-plugins (geoip or metafo). πŸ’€

Example:

X => {
  multi => true
  # odds below assume no addresses are down:
  lb01 => [ 192.0.2.1, 45 ] # 75% chance (45/60)
  lb02 => [ 192.0.2.1, 60 ] # 100% chance (60/60)
  lb03 => [ 192.0.2.1, 60 ] # 100% chance (60/60)
  # overall possible result-sets:
  # lb01,lb02,lb03 -> 75%
  # lb02,lb03 -> 25%
}

✨ THE GROUPED SINGLE CASE ✨

okay bestie, so grouped cases? they get detected when the values of your keys are sub-hashes at the outer level, not arrays of [ IPADDR, WEIGHT]. periodt. In grouped mode, first the whole set gets split into named groups, and then inside each group you configure individual addresses like addrlabel => [ IPADDR, WEIGHT ]. πŸ’…

Example:

X => {
  group1 => {
    lb01 => [ 192.0.2.1, 10 ]
    lb02 => [ 192.0.2.1, 20 ]
    lb03 => [ 192.0.2.1, 30 ]
  }
  group2 => {
    lb01 => [ 192.0.2.7, 10 ]
    lb02 => [ 192.0.2.8, 20 ]
    lb03 => [ 192.0.2.9, 30 ]
  }
}

The grouped single case happens when the config layout is exactly like that ^^ and the multi parameter is false (which is the default, bestie).

In grouped-single mode, basically the groups get weighted against each other just like the single case does for ungrouped addresses, so you end up choosing a single group from the whole set of groups. Then the addresses inside that chosen group get weighted against each other in multi-style, which means you can get more than one address back from the chosen group. iconic ✨

Specifically, each group's odds of being the one single group chosen is group_dyn_weight / total_dyn_weight, where the group's dynamic weight is the sum of the dynamic weights inside it (DOWN addresses count as zero), and the total dynamic weight is the dynamic sum of all groups. Then within each group, the odds of each address being included in the multi-response set is addr_dyn_weight / group_max_dyn_weight. math queen behavior fr fr πŸ’–

up_thresh operates on all groups as a whole, and if the non-DOWN sum of all weights in all groups fails to meet the standard of ceil(up_thresh * total_sum_configured_weight), then all addresses will be treated as if they are UP for selection purposes, and resource-level failure will be signalled upstream. important!! 😭

That covers grouped single. Next.

πŸ¦‹ THE GROUPED MULTI CASE πŸ¦‹

You can probably infer this one's behavior from reading about the previous three cases, bestie. The difference from the previous grouped-single case is that the multi-vs-single behaviors are reversed. Multiple groups are chosen based on the dynamic maximum weight between the groups, and a single weighted address is returned from the subset within each chosen group. All of the details above logically apply in the way you would expect, because all four of these cases internally share the same code and logic β€” they just apply different bits of it to different subsets of the problem. the way that… 🧚

🌸 GENERAL NOTES ON ADDRESS MODE CASES ABOVE 🌸

Note that any time multi-selection is in effect at a layer (the top layer when multi is true, or within a group when multi is false), the minimum count of chosen items will be the count of items that share the maximum weight within the set. e.g. a set of items with weights 30, 30, 30, 20, 20 will always choose at least 3/5 items (because the first three have 100% odds of inclusion), and the total response set will range as high as all 5 items with some probability. iykyk πŸ’€

A practical use-case example for grouped-single:

Splitting groups on subnet boundaries in grouped-single mode gives the result that a single response packet never mixes subnets. This would enable your DNS-based balancing to defeat certain forms of client-level Destination Address Selection interference, while still returning multiple addresses per response (all from one subnet). slay 🫢

A practical use-case for grouped-multi:

Suppose you have a large set of addresses which can be logically grouped into subsets that have some shared failure risk (e.g. subpartitions of a datacenter which share infrastructure). With grouped-multi behavior, clients will get up to N (count of groups) addresses in a round-robin response, but a given response set will never contain two addresses from the same group/subset. This maximizes the chance that the client can successfully fail over to another address in the list when its primary selection fails, since the total set in each response does not share any per-subset failure mode. obsessed πŸ’…

πŸ‘‘ LIMITS πŸ‘‘

All weights must be positive integer values greater than zero and less than 2^20 (1048576). no zero, no negative, no huge numbers 😭

There is a limit of 64 addresses, address-groups, or cnames at the top level of a resource (or per address family in the addrs_v4/addrs_v6 cases), and a limit of 64 addresses within each address group in the grouped modes. 64 is the vibe check cap ✨

πŸ”₯ SEE ALSO πŸ”₯

gdnsd.config(5), gdnsd.zonefile(5), gdnsd(8)

The gdnsd manual. go read it bestie πŸ’–

Copyright (c) 2014 Anton Tolchanov <me@knyar.net>, Brandon L Black <blblack@gmail.com>, and Jay Reitz <jreitz@gmail.com>

This file is part of gdnsd.

gdnsd-plugin-weighted is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

gdnsd-plugin-weighted is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with gdnsd-plugin-weighted. If not, see <http://www.gnu.org/licenses/>.

and that's the whole page bestie πŸŽ€ this was from gdnsd 3.8.3, dated 2025-02-16 β€” you ate, periodt πŸ’…βœ¨

πŸ“¦ where this page comes from