Module resty.dns.balancer.base

Base-balancer.

The base class for balancers. It implements DNS resolution and fanning out hostnames to addresses. It builds and maintains a tree structure:

balancer <1 --- many> hosts <1 --- many> addresses

Updating the DNS records is active, meaning that a background task is running to periodically update DNS records when they expire. This only applies to the hostnames added to the balancer, not to any nested DNS records.

Weights

Weights will be tracked as follows. Since a Balancer has multiple Hosts, and a Host has multiple Addresses. The Host weight will be the sum of all its addresses, and the Balancer weight will be the sum of all Hosts. See addHost on how to set the weight for an address.

The weight of each address will be the weight provided as nodeWeight when adding a host. So adding a host with weight 20, that resolves to 2 IP addresses, will insert 2 addresses each with a weight of 20, totalling the weight of the host to 40.

Exception 1: If the host resolves to an SRV record, in which case each address gets the weight as specified in the DNS record. In this case the nodeWeight property will be ignored.

Exception 2: If the DNS record for the host has a ttl=0 then the record contents will be ignored, and a single address with the original hostname will be inserted. This address will get a weight assigned of nodeWeight. Whenever the balancer hits this address, it will be resolved on the spot, hence honouring the ttl=0 value.

Adding and resolving hosts

When adding a host, it will be resolved and for each entry an address will be added. With the exception of a ttl=0 setting as noted above. When resolving the names, any CNAME records will be dereferenced immediately, any other records will not.

Example 1: add an IP address "127.0.0.1"

The host object will resolve the name, and since it's and IP address it returns a single A record with 1 entry, that same IP address.

host object 1   : hostname="127.0.0.1"  --> since this is the name added
address object 1: ip="127.0.0.1"        --> single IP address

Example 2: complex DNS lookup chain

assuming the following lookup chain for a host added by name "myhost":

myhost    --> CNAME yourhost
yourhost  --> CNAME herhost
herhost   --> CNAME theirhost
theirhost --> SRV with 2 entries: host1.com, host2.com
host1.com --> A with 1 entry: 192.168.1.10
host2.com --> A with 1 entry: 192.168.1.11

Adding a host by name myhost will first create a host by name myhost. It will then resolve the name myhost, the CNAME chain will be dereferenced immediately, so the result will be an SRV record with 2 named entries. The names will be used for the addresses:

host object 1   : hostname="myhost"
address object 1: ip="host1.com"  --> NOT an ip, but a name!
address object 2: ip="host2.com"  --> NOT an ip, but a name!

When the balancer hits these addresses (when calling getPeer), it will dereference them (so they will be resolved at balancer-runtime, not at balancer-buildtime).

There is another special case, in case a record has a ttl=0 setting. In that case a "fake" SRV record is inserted, to make sure we honour the ttl=0 and resolve on each getPeer invocation, without altering the balancer every time. Here's an example:

myhost    --> A with 1 entry: 192.168.1.10, and TTL=0

Internally it is converted into a fake SRV record which results in the following balancer structure:

host object 1   : hostname="myhost"
address object 1: ip="myhost"  --> NOT an ip, but a name!

This in turn will result in DNS resolution on each call for an IP, and hence will make sure the ttl=0 setting is effectively being used.

Handle management

handles are used to retain state between consecutive invocations (calls to the objBalancer:getPeer method). The handles are re-used and tracked for garbage collection. There are two uses:

  1. tracking progress (eg. keeping a retry count)
  2. tracking resources (eg. with least connections a handle 'owns' 1 connection, to be released when the connection is finished)

The basic flow and related responsibilities:

  • user code calls getPeer to get an ip/port/hostname according to the load balancing algorithm.
  • getPeer both takes a handle (on a retry), and returns it (on success).
  • handles are managed by the base balancer, and getPeer can call objBalancer:getHandle to get one.
  • on a retry getPeer should call objAddress:release(handle, ignore) to release the previous (failed) try, and it should clear the handle.address field.
  • on success getPeer should set handle.address with the address object that returned the ip, port, and hostname, and return the handle with those.
  • at the end of the connection life cycle the user code should call handle:release to release the resources, and/or collect necessary statistics.
  • as a safety check the handles will have a GC method attached. So in case they are not explicitly released the (default) GC handler will call handle.address:release(handle, true) to make sure no resources leak.

Clustering

The base-balancer is deterministic in the way it adds/removes elements. So as long as the confguration is the same, and adding/removing hosts is done in the same order the exact same balancer will be created. This is important in case of consistent-hashing approaches, since each cluster member needs to behave the same.

NOTE: there is one caveat, DNS resolution is not deterministic, because timing differences might cause different orders of adding/removing. Hence the structures can potentially slowly diverge. If this is unacceptable, make sure you do not invlove DNS by adding hosts by their IP adresses instead of their hostname.

Housekeeping

The balancer does some house keeping and may insert some extra fields in dns records. Those fields will have an __ prefix (double underscores).

Info:

  • Copyright: 2016-2020 Kong Inc. All rights reserved.
  • License: Apache 2.0
  • Author: Thijs Schreijer

Implementation properties

objBalancer.addresses List of addresses.
objBalancer.hosts List of hosts.
objBalancer:afterHostUpdate (host) This method is called after changes have been made to the addresses.
objBalancer:beforeHostDelete (host) This method is called after a host is being removed from the balancer.
objBalancer:getHandle (gc_handler, release_handler) Gets a handle to be returned by getPeer.
objBalancer:newAddress (addr) Creates a new address object.
objBalancer:newHost (host) Creates a new host object.
objBalancer:onAddAddress (address) This method is called after an address is being added to the balancer.
objBalancer:onRemoveAddress (address) This method is called after an address has been deleted from the balancer.

User properties

new (opts) Creates a new base balancer.
objBalancer:addHost (hostname, port, nodeWeight) Adds a host to the balancer.
objBalancer:getPeer (cacheOnly, handle, hashValue) Gets the next ip address and port according to the loadbalancing scheme.
objBalancer:getStatus () Gets the status of the balancer.
objBalancer:removeHost (hostname, port) Removes a host from the balancer.
objBalancer:setAddressStatus (available, ip_address_handle, port, hostname) Sets the current status of an address.
objBalancer:setCallback (callback) Sets an event callback for user code.
objBalancer:setHostStatus (available, hostname, port) Sets the status of a host.


Implementation properties

These properties are only relevant for implementing a new balancer algorithm using this base class. To use a balancer see the User properties section.
objBalancer.addresses
List of addresses. This is a list of addresses, ordered based on when they were added.
objBalancer.hosts
List of hosts. This is a list of addresses, ordered based on when they were added.
objBalancer:afterHostUpdate (host)
This method is called after changes have been made to the addresses.

When implementing a new balancer algorithm, you might want to override this method.

The call is after the addition of new, and disabling old, but before deleting old addresses. The address.disabled field will be true for addresses that are about to be deleted.

Parameters:

  • host the host object that had its addresses updated
objBalancer:beforeHostDelete (host)
This method is called after a host is being removed from the balancer.

When implementing a new balancer algorithm, you might want to override this method.

The call is after disabling, but before deleting the associated addresses. The address.disabled field will be true for addresses that are about to be deleted.

Parameters:

  • host the host object about to be deleted
objBalancer:getHandle (gc_handler, release_handler)

Gets a handle to be returned by getPeer. A handle will have two functions attached to it:

  1. the release handler. This can be called from user code as handle:release(...). The default handler will be handle:release(ignore) and will call handle.address:release(handle, ignore), and then release the handle itself, so it can be reused.

  2. the GC handler, which is a fallback in case the handle wasn't released by user code. It will be called as gc_handler(handle) and the default implementation will call handle.address:release(handle, true).

Parameters:

  • gc_handler (optional) a custom GC method for when the handle is not explicitly released.
  • release_handler (optional) a custom release method to release the resources when the request cycle is complete.

Returns:

    handle
objBalancer:newAddress (addr)

Creates a new address object. There is no need to call this from user code. When implementing a new balancer algorithm, you might want to override this method. The addr table should contain:

  • ip: the upstream ip address or target name
  • port: the upstream port number
  • weight: the relative weight for the balancer algorithm
  • host: the host object the new address belongs to

Parameters:

  • addr table to be transformed to Address object

Returns:

    new address object, or error on bad input
objBalancer:newHost (host)

Creates a new host object. There is no need to call this from user code. When implementing a new balancer algorithm, you might want to override this method. The host table should have fields:

  • hostname: the upstream hostname (as used in dns queries)
  • port: the upstream port number for A and AAAA dns records. For SRV records the reported port by the DNS server will be used.
  • nodeWeight: the relative weight for the balancer algorithm to assign to each A or AAAA dns record. For SRV records the reported weight by the DNS server will be used.
  • balancer: the balancer object the host belongs to

Parameters:

  • host table to create the host object from.

Returns:

    new host object, or error on bad input.
objBalancer:onAddAddress (address)
This method is called after an address is being added to the balancer.

When implementing a new balancer algorithm, you might want to override this method.

Parameters:

  • address
objBalancer:onRemoveAddress (address)
This method is called after an address has been deleted from the balancer.

When implementing a new balancer algorithm, you might want to override this method.

Parameters:

  • address

User properties

new (opts)

Creates a new base balancer.

A single balancer can hold multiple hosts. A host can be an ip address or a name. As such each host can have multiple addresses (or actual ip+port combinations).

The options table has the following fields;

  • dns (required) a configured dns.client object for querying the dns server.
  • requery (optional) interval of requerying the dns server for previously failed queries. Defaults to 30 if omitted (in seconds)
  • ttl0 (optional) Maximum lifetime for records inserted with ttl=0, to verify the ttl is still 0. Defaults to 60 if omitted (in seconds)
  • callback (optional) a function called when an address is added/changed. See setCallback for details.
  • log_prefix (optional) a name used in the prefix for log messages. Defaults to "balancer" which results in log prefix "[balancer 1]" (the number is a sequential id number)
  • healthThreshold (optional) minimum percentage of the balancer weight that must be healthy/available for the whole balancer to be considered healthy. Defaults to 0% if omitted.
  • useSRVname (optional) if truthy, then in case of the hostname resolving to an SRV record with another level of names, the returned hostname by getPeer will not be the name of the host as added, but the name of the entry in the SRV record.

Parameters:

  • opts table with options

Returns:

    new balancer object or nil+error
objBalancer:addHost (hostname, port, nodeWeight)
Adds a host to the balancer. The name will be resolved and for each DNS entry an address will be added.

Within a balancer the combination of hostname and port must be unique, so multiple calls with the same target will only update the weight of the existing entry.

Parameters:

  • hostname the hostname/ip to add. It will be resolved and based on that 1 or more addresses will be added to the balancer.
  • port the port to use for the addresses. If the hostname resolves to an SRV record, this will be ignored, and the port will be taken from the SRV record.
  • nodeWeight the weight to use for the addresses. If the hostname resolves to an SRV record, this will be ignored, and the weight will be taken from the SRV record.

Returns:

    balancer object, or throw an error on bad input
objBalancer:getPeer (cacheOnly, handle, hashValue)
Gets the next ip address and port according to the loadbalancing scheme. If the dns record attached to the requested address is expired, then it will be renewed and as a consequence the balancer algorithm might be updated.

Parameters:

  • cacheOnly If truthy, no dns lookups will be done, only cache.
  • handle the handle returned by a previous call to getPeer. This will retain some state over retries. See also setAddressStatus.
  • hashValue (optional) number for consistent hashing, if supported by the algorithm. The hashValue must be an (evenly distributed) integer >= 0.

Returns:

    ip + port + hostheader + handle, or nil+error

Usage:

    -- get an IP address
    local ip, port, hostheader, handle = b:getPeer()
    
    -- go do the connection stuff here...
    
    -- on a retry do:
    ip, port, hostheader, handle = b:getPeer(true, handle)  -- pass in previous 'handle'
    
    -- go try again
    
    -- when it finally fails
    handle:release(true)  -- release resources, but ignore stats
    
    -- on a successful connection
    handle:release()  -- release resources, and collect stats
objBalancer:getStatus ()
Gets the status of the balancer. This reports the full structure of the balancer state, including hosts, addresses, weights, and availability.

Returns:

    table with balancer status
objBalancer:removeHost (hostname, port)
Removes a host from the balancer. All associated addresses will be deleted, causing updates to the balancer algorithm. Will not throw an error if the hostname is not in the current list.

Parameters:

  • hostname hostname to remove
  • port port to remove (optional, defaults to 80 if omitted)

Returns:

    balancer object, or throws an error on bad input
objBalancer:setAddressStatus (available, ip_address_handle, port, hostname)
Sets the current status of an address. This allows to temporarily suspend peers when they are offline/unhealthy, it will not modify the address held by the record. The parameters passed in should be previous results from getPeer. Call this either as:

  • setAddressStatus(available, address),
  • setAddressStatus(available, handle), or as
  • setAddressStatus(available, ip, port, hostname)

Using the address or handle is preferred since it is guaranteed to match. By ip/port/name might fail if there are too many DNS levels.

Parameters:

  • available true for enabled/healthy, false for disabled/unhealthy
  • ip_address_handle ip address of the peer, the address object, or the handle.
  • port the port of the peer (in address object, not as recorded with the Host!)
  • hostname (optional, defaults to the value of ip) the hostname

Returns:

    true on success, or nil+err if not found
objBalancer:setCallback (callback)
Sets an event callback for user code. The callback is invoked for every address added to/removed from the balancer, and on health changes.

Signature of the callback is for address adding/removing:

function(balancer, "added"/"removed", address, ip, port, hostname, hostheader)

  • address is the address object added
  • ip is the IP address for this object, but might also be a hostname if the DNS resolution returns another name (usually in SRV records)
  • port is the port to use
  • hostname is the hostname for which the address was added to the balancer with addHost (resolving that name caused the creation of this address)
  • hostheader is the hostheader to be used. This can have 3 values; 1) nil if the hostname added was an ip-address to begin with, 2) it will be equal to the name in ip if there is a named SRV entry, and useSRVname == true, 3) otherwise it will be equal to hostname

For health updates the signature is:

function(balancer, "health", isHealthy)

NOTE: the callback will be executed async (on a timer) so maybe executed only after the methods (addHost and removeHost for example) have returned.

Parameters:

  • callback a function called when an address is added/removed

Returns:

    true, or throws an error on bad input
objBalancer:setHostStatus (available, hostname, port)
Sets the status of a host. This will switch all underlying address objects to the specified state.

Parameters:

  • available true for enabled/healthy, false for disabled/unhealthy
  • hostname name by which it was added to the balancer.
  • port the port of the host by which it was added to the balancer (optional, defaults to 80 if omitted).

Returns:

    true on success, or nil+err if not found
generated by LDoc 1.4.6 Last updated 2021-07-06 11:55:24