Adding a new configuration variable in records.config requires a number
of steps which are mostly documented here.

Before adding a new configuration variable, please discuss it on the mailing
list. It will commonly be the case that a better name, or a more general
approach to the problem which solves several different issues, may be suggested.

To begin, the new configuration variables must be added to RecordsConfig.cc.
This contains a long array of configuration variable records. The fields for
each record are:

type:RecT

Type of record. The valid values are:

RECT_NULL

Undefined record.

RECT_CONFIG

General configuration variable.

RECT_PROCESS

Process related statistic.

RECT_NODE

Local statistic.

RECT_PLUGIN

Plugin created statistic.

In general, RECT_CONFIG should be used.

name:charconst*

The fully qualified name of the configuration variable. Although there
appears to be a hierarchical naming scheme, that’s just a convention, and it
is not actually used by the code. Nonetheless, new variables should adhere
to the hierarchical scheme.

value_type:RecDataT

The data type of the value. It should be one of RECD_INT,
RECD_STRING, RECD_FLOAT as appropriate.

default:charconst*

The default value for the variable. This is always a string regardless of
the value_type.

update:RecUpdateT

Information about how the variable is updated. The valid values are:

RECU_NULL

Behavior is unknown or unspecified.

RECU_DYNAMIC

This can be updated via command line tools.

RECD_RESTART_TS

The traffic_server process must be restarted for a new value to take effect.

RECD_RESTART_TM

The traffic_manager process must be restarted for a new value to take effect.

required:RecordRequiredType

Effectively a boolean that specifies if the record is required to be present,
with RR_NULL meaning not required and RR_REQUIRED indicating that it
is required. Given that using RR_REQUIRED would be a major
incompatibility, RR_NULL is generally the better choice.

check:RecCheckT

Additional type checking. It is unclear if this is actually implemented. The
valid values are:

RECC_NULL

No additional checking.

RECC_STR

Verify the value is a string.

RECC_INT

Verify the value is an integer.

RECC_IP

Verify the value is an IP address. Unknown if this checks for IPv6.

pattern:charconst*

This provides a regular expressions (PCRE format) for validating the value,
beyond the basic type validation performed by RecCheckT. This can be
NULL if there is no regular expression to use.

access:RecAccessT

Access control. The valid values are:

RECA_NULL

The value is read / write.

RECA_READ_ONLY

The value is read only.

RECA_NO_ACCESS

No access to the value; only privileged level parts of ATS can access the
value.

The primary effort in defining a configuration variable is handling updates,
generally via traffic_ctlconfigreload. This is handled in a generic way, as
described in the next section, or in a more specialized way
(built on top of the generic mechanism) for HTTP related configuration
variables. This is only needed if the variable is marked as dynamically
updatable (RECU_DYNAMIC) although HTTP configuration variables should be
dynamic if possible.

A configuration variable should be documented in records.config. There
are many examples in the file already that can be used for guidance. The general
format is to use the tag

.. ts:cv:`variable.name.here`

The arguments to this are the same as for the configuration file. The
documentation generator will pick out key bits and use them to decorate the
entry. In particular if a value is present it will be removed and used as the
default value. You can attach some additional options to the variable. These
are:

reloadable

The variable can be reloaded via command line on a running Traffic Server.

metric

Specify the units for the value. This is critical for variables that use unexpected or non-obvious metrics, such as minutes instead of seconds, or disk sectors instead of bytes.

deprecated

Mark a variable as deprecated.

Example

:ts:cv:`custom.variable`

reloadable

units

minutes

deprecated

If you need to refer to another configuration variable in the documentation, you
can use the form

:ts:cv:`the.full.name.of.the.variable`

This will display the name as a link to the full definition.

In general, a new configuration variable should not be present in the default
records.config. If it is added, such defaults should be added to the
file proxy/config/records.config.default.in. This is used to generate the
default records.config. Just add the variable to the file in an
appropriate place with a proper default as this will now override whatever
default you put in the code for new installs.

The simplest mechanism for handling updates is the REC_EstablishStaticConfigXXX
family of functions. This mechanism will cause the value in the indicated
instance to be updated in place when an update to records.config occurs.
This is done asynchronously using atomic operations. Use of these variables must
keep that in mind.

If a variable requires additional handling when updated a callback can be
registered which is called when the variable is updated. This is what the
REC_EstablishStaticConfigXXX calls do internally with a callback that simply
reads the new value and writes it to storage indicated by the call parameters.
The functions used are the link_XXX static functions in RecCore.cc.

To register a configuration variable callback, call RecRegisterConfigUpdateCb
with the arguments:

charconst*name

The variable name.

callback

A function with the signature <int(charconst*name,RecDataTtype,RecDatadata,void*cookie)>.
The name value passed is the same as the name passed to the
registration function as is the cookie argument. The type and
data are the new value for the variable. The return value is currently
ignored. For future compatibility return REC_ERR_OKAY.

void*cookie

A value passed to the callback. This is only for the callback, the
internals simply store it and pass it on.

callback is called under lock so it should be quick and not block. If that is
necessary a continuation should be scheduled to handle the required
action.

Note

The callback occurs asynchronously. For HTTP variables as described in the
next section, this is handled by the more specialized HTTP update mechanisms.
Otherwise it is the implementer’s responsibility to avoid race conditions.

Variables used for HTTP processing should be declared as members of the
HTTPConfigParams structure (but see Overridable Variables for
further details) and use the specialized HTTP update mechanisms which handle
synchronization and initialization issues.

The configuration logic maintains two copies of the HTTPConfigParams
structure, the master copy and the current copy. The master copy is kept in the
m_master member of the HttpConfig singleton. The current copy is kept in
the ConfigProcessor. The goal is to provide a (somewhat) atomic update for
configuration variables which are loaded individually in to the master copy as
updates are received and then bulk copied to a new instance which is then
swapped in as the current copy. The HTTP state machine interacts with this
mechanism to avoid race conditions.

For each variable, a mapping between the variable name and the appropriate
member in the master copy should be established between in the HTTPConfig::startup
method. The HttpEstablishStaticConfigXXX functions should be used unless
there is a strong, explicit reason to not do so.

The HTTPConfig::reconfigure method handles the current copy of the HTTP
configuration variables. Logic should be added here to copy the value from the
master copy to the current copy. Generally this will be a simple assignment. If
there are dependencies between variables, those should be checked and enforced
in this method.

HTTP related variables that are changeable per transaction are stored in the
OverridableHttpConfigParams structure, an instance of which is the oride
member of HTTPConfigParams and therefore the points in the previous section
still apply. The only difference for that is the further .oride member specifier in the structure references.

The variable is required to be accessible from the transaction API. In addition
to any custom API functions used to access the value, the following items are
required for generic access:

Add a value to the TSOverridableConfigKey enumeration in apidefs.h.in.

Augment the TSHttpTxnConfigFind function to return this enumeration value
when given the name of the configuration variable. Be sure to count the
characters very carefully.

Augment the _conf_to_memberp function in InkAPI.cc to return a pointer
to the appropriate member of OverridableHttpConfigParams and set the type
if not a byte value.

Update the testing logic in InkAPITest.cc by adding the string name of the
configuration variable to the SDK_Overridable_Configs array.

A relatively new feature for overridable variables is the ability to keep them in more natural data
types and convert as needed to the API types. This in turns enables defining the configuration
locally in a module and then “exporting” it to the API interface. Modules then do not have to
include headers for all types in all overridable configurations.

The conversion is done through an instance of MgmtConverter. This has 6 points to
conversions, a load and store function for each of the types MgmtInt, MgmtFloat, and
MgmtInt. The MgmtByte type is handled by the MgmtInt conversions. In general
each overridable variable will specify two of these, a load and store for a specific type, although
it is possible to provide other pairs, e.g. if a value is an enumeration can should be settable
as a string as well as an integer.

The module is responsible for creating an instance of MgmtConverter with the appropriate
load / store function pairs set. The declaration must be visible in the proxy/InkAPI.cc
file. The function _conf_to_memberp sets up the conversion. For the value of the enumeration
TSOverridableConfigKey that specifies the overridable variable, code is added to specify
the member and the conversion. There are default converters for the API types and if the overridable
is one of those, it is only necessary to call _memberp_to_generic passing in a pointer to
the variable. For a variable with conversion, ret should be set to point to the variable and
conv set to point to the converter for that variable. If multiple variables are of the same
type they can use the same converter because a pointer to the specific member is passed to the
converter.