Current section

Files

Jump to
cloudi_core src cloudi_core_i_configuration.hrl
Raw

src/cloudi_core_i_configuration.hrl

%-*-Mode:erlang;coding:utf-8;tab-width:4;c-basic-offset:4;indent-tabs-mode:()-*-
% ex: set ft=erlang fenc=utf-8 sts=4 ts=4 sw=4 et nomod:
%%%
%%%------------------------------------------------------------------------
%%%
%%% MIT License
%%%
%%% Copyright (c) 2009-2017 Michael Truog <mjtruog at gmail dot com>
%%%
%%% Permission is hereby granted, free of charge, to any person obtaining a
%%% copy of this software and associated documentation files (the "Software"),
%%% to deal in the Software without restriction, including without limitation
%%% the rights to use, copy, modify, merge, publish, distribute, sublicense,
%%% and/or sell copies of the Software, and to permit persons to whom the
%%% Software is furnished to do so, subject to the following conditions:
%%%
%%% The above copyright notice and this permission notice shall be included in
%%% all copies or substantial portions of the Software.
%%%
%%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
%%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
%%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
%%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
%%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
%%% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
%%% DEALINGS IN THE SOFTWARE.
%%%
%%%------------------------------------------------------------------------
-include("cloudi_core_i_configuration_defaults.hrl").
-record(config_logging_syslog,
{
identity = "CloudI"
:: cloudi_service_api:logging_syslog_identity(),
facility = local0
:: cloudi_service_api:logging_syslog_facility(),
% The mapping for CloudI levels to syslog priorities is:
% fatal -> critical (2)
% error -> error (3)
% warn -> warning (4)
% info -> notice (5)
% debug -> informational (6)
% trace -> debug (7)
level = trace
:: cloudi_service_api:loglevel(),
transport = local
:: cloudi_service_api:logging_syslog_transport(),
transport_options = []
:: cloudi_service_api:logging_syslog_transport_options(),
protocol = rfc3164
:: cloudi_service_api:logging_syslog_protocol(),
path = "/dev/log"
:: cloudi_service_api:logging_syslog_path(),
host = {127,0,0,1}
:: cloudi_service_api:logging_syslog_host(),
port = undefined
:: cloudi_service_api:logging_syslog_port()
}).
-record(config_logging_formatter,
{
% The mapping for lager levels to CloudI levels is:
% (use CloudI levels to avoid the mapping)
% emergency -> fatal
% alert (becomes emergency) -> fatal
% critical (becomes emergency) -> fatal
% error -> error
% warning -> warn
% notice (becomes warning) -> warn
% info -> info
% debug -> debug
% -> trace
% none -> off
level = trace
:: cloudi_service_api:loglevel(),
output = undefined
:: undefined | module(),
output_name
:: atom(),
output_args = [] % provided to output module with formatter args
:: list(),
output_max_r = ?DEFAULT_MAX_R
:: non_neg_integer(),
output_max_t = ?DEFAULT_MAX_T
:: cloudi_service_api:seconds(),
formatter = undefined
:: undefined | module(),
formatter_config = []
:: list()
}).
-record(config_logging_formatters,
{
% 'any' formatter entry
default = undefined
:: undefined | #config_logging_formatter{},
% nonempty_list(module()) -> #config_logging_formatter{} lookup
lookup = keys1value:new(maps)
:: keys1value:
keys1value(module(), #config_logging_formatter{}),
level = undefined
:: undefined | cloudi_service_api:loglevel()
}).
-record(config_logging,
{
% file path to write log output to while allowing the file to rotate
file = "logs/cloudi.log"
:: undefined | string(),
% write log output to stdout
stdout = false
:: boolean(),
% controls both file and stdout
level = trace
:: undefined | cloudi_service_api:loglevel(),
% redirect log output to a different CloudI node
redirect = undefined
:: undefined | node(),
% send log output to syslog
syslog = undefined
:: undefined | #config_logging_syslog{},
% use custom formatters for log output
formatters = undefined
:: undefined | #config_logging_formatters{},
% aspect functions to execute before logging
aspects_log_before = []
:: list(cloudi_service_api:aspect_log_before()),
% aspect functions to execute after logging
aspects_log_after = []
:: list(cloudi_service_api:aspect_log_after())
}).
-record(config_service_options,
{
% DEFAULT VALUES ASSIGNED BELOW
% Relevant for both Internal and External Services in the same way:
% -128 (high) <= priority_default <= 127 (low)
priority_default = ?DEFAULT_PRIORITY
:: cloudi_service:priority(),
% a limit on the total number of incoming service requests that
% are queued while the service is busy (limits memory consumption)
queue_limit = undefined
:: undefined | non_neg_integer(),
% a limit on the total amount of memory incoming service requests may
% consume within the queue while the service is busy,
% (configured in kilobytes and stored as bytes)
queue_size = undefined
:: undefined | pos_integer(),
% service reqs/second maximum
rate_request_max = undefined
:: undefined | number() |
list({period, cloudi_service_api:period_seconds()} |
{value, number()}) |
tuple(),
% delay after startup before requesting the initial service
% group membership (when using a lazy destination refresh method)
dest_refresh_start = ?DEFAULT_DEST_REFRESH_START
:: cloudi_service_api:dest_refresh_delay_milliseconds(),
% maximum possible time for a service death to remove service
% group membership when using a lazy destination refresh method
% (not an immediate destination refresh method).
% a lazy destination refresh method is used when a
% service is mainly communicating with long-lived services
% (and an immediate destination refresh method is used when
% a service is mainly communicating with short-lived services).
dest_refresh_delay = ?DEFAULT_DEST_REFRESH_DELAY
:: cloudi_service_api:dest_refresh_delay_milliseconds(),
% should the service request name lookup be a synchronous or
% an asynchronous operation (the default is to be synchronous and
% keep the service name lookup result decoupled from the
% destination service's lifetime during the service request
% timeout period, to anticipate any number of delays a service
% request may encounter, including node-splits and forwards,
% to provide service failure isolation)
request_name_lookup = ?DEFAULT_REQUEST_NAME_LOOKUP
:: sync | async,
% should the service request handler execution time decrement the
% request timeout to reduce the timeout of a forwarded request or
% the timeout of a returned response
% (if the request timeout is equal to the forward or return timeout,
% n.b., doesn't adjust the timeout of a cloudi_service:return_nothrow)
request_timeout_adjustment = false
:: boolean(),
% defines the max request timeout considered to be "immediate":
% max timeout value of sent service requests whose destination
% Erlang pid will not be monitored because the rate at which
% sent service requests are being sent to unresponsive
% destination Erlang pids will not cause excessive timer
% (erlang:send_after/3) memory consumption during the time period
% specified by this value. sent service requests with timeouts
% that are greater than or equal to this value will have their
% destination Erlang pid monitored so that timer memory consumption
% is cleaned up quicker than the timeout value specified within the
% service request. as the rate of sent service requests increases
% to unresponsive services, this value will need to decrease,
% to affect service requests of shorter duration.
request_timeout_immediate_max = 20001 % milliseconds
:: cloudi_service_api:request_timeout_immediate_max_milliseconds(),
% should the service use internal timeout information to provide a
% more accurate timeout value within the response provided
% (n.b., this only affects the response timeout of a successful
% send_async request)
response_timeout_adjustment = false
:: boolean(),
% defines the max response timeout considered to be "immediate":
% max timeout value of a returned null response which is discarded
% rather than being returned, so that the associated service request
% timeout is caused by the sending service's service request timer
% instead of a returned null response message. service request
% null responses with timeouts that are greater than or equal to this
% value will always be sent to help the sending-side avoid excessive
% delays during the timeout period.
response_timeout_immediate_max = 20001 % milliseconds
:: cloudi_service_api:response_timeout_immediate_max_milliseconds(),
% should the process count be varied automatically based on the
% rate of service processing within a specific time period.
% the count max/min specify limits for the count_process changes
% as either floating point percentages (the result is rounded) or
% as integer absolutes.
count_process_dynamic = false
:: false |
list({period, cloudi_service_api:period_seconds()} |
{rate_request_max, number()} | % service reqs/second
{rate_request_min, number()} | % service reqs/second
{count_max, number()} | % float multiplier or
{count_min, number()}) | % integer absolute
tuple(),
% when undefined, timeout_terminate defaults to
% (1000 * MaxT) / MaxR - 100 to ensure the service lifetime is finite
% when errors occur. timeout_terminate can be set manually to
% enforce a greater uptime constraint on the service execution.
timeout_terminate = undefined
:: undefined |
cloudi_service_api:timeout_terminate_milliseconds(),
% delay to wait after a service terminate but before the service
% initialization of the new service instance, during a service restart
restart_delay = false
:: list({time_exponential_min,
cloudi_service_api:restart_delay_milliseconds()} |
{time_exponential_max,
cloudi_service_api:restart_delay_milliseconds()} |
{time_absolute,
cloudi_service_api:restart_delay_milliseconds()}) |
false |
tuple(),
% provide a scope for all subscribe/unsubscribe and messaging
% (i.e., all service name usage is within the scope). Using a
% different scope can help avoid contention when using an immediate
% destination refresh method.
scope = ?DEFAULT_SCOPE
:: atom(),
% add latency to all service requests and info messages received
% based on the parameters specified. If "system" is set, the
% cloudi_core Erlang application env value is used after being
% checked during service startup (e.g., after service restarts).
% (all time parameters are specified in milliseconds)
monkey_latency = false
:: list({time_uniform_min,
cloudi_service_api:latency_min_time_milliseconds()} |
{time_uniform_max,
cloudi_service_api:latency_max_time_milliseconds()} |
{time_gaussian_mean,
cloudi_service_api:latency_mean_time_milliseconds()} |
{time_gaussian_stddev, float() | pos_integer()} |
{time_absolute,
cloudi_service_api:latency_time_milliseconds()}) |
system | false |
tuple(),
% cause service termination based on the probability parameter
% (checked for each service request and info message, if necessary).
% If "system" is set, the cloudi_core Erlang application env value
% is used after being checked during service startup
% (e.g., after service restarts). The probability_day method
% replicates the Netflix chaos monkey usage.
monkey_chaos = false
:: list({probability_request, float()} |
{probability_day, float()}) |
system | false |
tuple(),
% should the service be automatically loaded and unloaded at
% service start and stop, respectively?
% (or the aspect module be automatically loaded)
automatic_loading = true
:: boolean(),
% what erlang:spawn_opt/2 options should be used, if any, by the
% service's long-lived service request sending Erlang process
dispatcher_pid_options = []
:: list(link |
{priority, low | normal | high} |
{fullsweep_after, non_neg_integer()} |
{min_heap_size, non_neg_integer()} |
{min_bin_vheap_size, non_neg_integer()} |
{max_heap_size,
cloudi_service_api:max_heap_size_options()} |
{sensitive, boolean()} |
{message_queue_data, off_heap | on_heap | mixed}),
% Relevant for both Internal and External Services, different values:
% aspects are functions provided to be processed before or after
% the service callback is executed (Aspect-Oriented Programming (AOP))
aspects_init_after = []
:: list(cloudi_service_api:aspect_init_after_internal() |
cloudi_service_api:aspect_init_after_external()),
aspects_request_before = []
:: list(cloudi_service_api:aspect_request_before_internal() |
cloudi_service_api:aspect_request_before_external()),
aspects_request_after = []
:: list(cloudi_service_api:aspect_request_after_internal() |
cloudi_service_api:aspect_request_after_external()),
aspects_info_before = []
:: list(cloudi_service_api:aspect_info_before_internal()),
aspects_info_after = []
:: list(cloudi_service_api:aspect_info_after_internal()),
aspects_terminate_before = []
:: list(cloudi_service_api:aspect_terminate_before_internal() |
cloudi_service_api:aspect_terminate_before_external()),
% Only Relevant for External Services:
% set resource limits for each OS process spawned
limit = []
:: cloudi_service_api:limit_external(),
% use a specific user and/or group for spawning the OS processes
owner = []
:: cloudi_service_api:owner_external(),
% set a nice value for each OS process spawned
nice = 0
:: cloudi_service_api:nice_external(),
% put each OS process spawned into a specific cgroup
cgroup = undefined
:: cloudi_service_api:cgroup_external(),
% set the root directory for spawning the OS process
chroot = undefined
:: cloudi_service_api:chroot_external(),
% set the current directory for spawning the OS process
directory = undefined
:: cloudi_service_api:directory_external(),
% Only Relevant for Internal Services:
% specify an Erlang application name, so it can be different from
% the CloudI service module name
application_name = undefined
:: atom(),
% what erlang:spawn_opt/2 options should be used, if any, by the
% service init handling Erlang process
init_pid_options = []
:: list(link |
{priority, low | normal | high} |
{fullsweep_after, non_neg_integer()} |
{min_heap_size, non_neg_integer()} |
{min_bin_vheap_size, non_neg_integer()} |
{max_heap_size,
cloudi_service_api:max_heap_size_options()} |
{sensitive, boolean()} |
{message_queue_data, off_heap | on_heap | mixed}),
% how many service requests should restart the Erlang process used for
% handling the service requests
% (an integer greater than 0 or the atom 'infinity' are valid values)
request_pid_uses = 1
:: infinity | pos_integer(),
% what erlang:spawn_opt/2 options should be used, if any, by the
% service request handling Erlang process
request_pid_options = []
:: list(link |
{priority, low | normal | high} |
{fullsweep_after, non_neg_integer()} |
{min_heap_size, non_neg_integer()} |
{min_bin_vheap_size, non_neg_integer()} |
{max_heap_size,
cloudi_service_api:max_heap_size_options()} |
{sensitive, boolean()} |
{message_queue_data, off_heap | on_heap | mixed}),
% how many info messages should restart the Erlang process used for
% handling the info message
% (an integer greater than 0 or the atom 'infinity' are valid values)
info_pid_uses = infinity
:: infinity | pos_integer(),
% what erlang:spawn_opt/2 options should be used, if any, by the
% info message handling Erlang process
info_pid_options = []
:: list(link |
{priority, low | normal | high} |
{fullsweep_after, non_neg_integer()} |
{min_heap_size, non_neg_integer()} |
{min_bin_vheap_size, non_neg_integer()} |
{max_heap_size,
cloudi_service_api:max_heap_size_options()} |
{sensitive, boolean()} |
{message_queue_data, off_heap | on_heap | mixed}),
% use two Erlang processes instead of one for an internal service to
% keep send operations separate from receive operations. better
% throughput can be achieved with duo_mode, especially when sending to
% external services (the mean request latency should become half
% due to the Erlang processes splitting the processing). duo_mode was
% the default for pre-v1.2.0 internal services. the second process
% is used in place of the info_pid and the process' message queue
% is used directly (so info_pid_uses must be set to infinity when
% duo_mode is true).
duo_mode = false
:: boolean(),
% should a mostly idle service hibernate automatically to conserve
% memory at the expense of extra garbage collections and an empty
% stack trace. if a list is provided, hibernate will occur when
% the rate of service processing drops below the minimum specified.
hibernate = false
:: boolean() |
list({period, cloudi_service_api:period_seconds()} |
{rate_request_min, number()}) | % service reqs/second
tuple(),
% should the service be reloaded automatically when an Erlang module
% file changes? should only be used during service development.
reload = false
:: boolean()
}).
% internal service parameters
-record(config_service_internal,
{
prefix
:: string(),
module
:: atom() | file:filename(),
file_path
:: undefined | file:filename(), % if module a path
args
:: list(),
dest_refresh
:: cloudi_service_api:dest_refresh(),
timeout_init
:: cloudi_service_api:timeout_initialize_value_milliseconds(),
timeout_async
:: cloudi_service_api:timeout_send_async_value_milliseconds(),
timeout_sync
:: cloudi_service_api:timeout_send_sync_value_milliseconds(),
timeout_term
:: cloudi_service_api:timeout_terminate_value_milliseconds(),
dest_list_deny
:: cloudi_service_api:dest_list(),
dest_list_allow
:: cloudi_service_api:dest_list(),
count_process
:: pos_integer(),
max_r
:: non_neg_integer(),
max_t
:: cloudi_service_api:seconds(),
options
:: #config_service_options{},
uuid
:: cloudi_service_api:service_id()
}).
% external service parameters
-record(config_service_external,
{
prefix
:: string(),
file_path
:: file:filename(),
args
:: string(),
env
:: list({string(), string()}),
dest_refresh
:: cloudi_service_api:dest_refresh(),
protocol
:: 'local' | 'tcp' | 'udp',
buffer_size
:: pos_integer(),
timeout_init
:: cloudi_service_api:timeout_initialize_value_milliseconds(),
timeout_async
:: cloudi_service_api:timeout_send_async_value_milliseconds(),
timeout_sync
:: cloudi_service_api:timeout_send_sync_value_milliseconds(),
timeout_term
:: cloudi_service_api:timeout_terminate_value_milliseconds(),
dest_list_deny
:: cloudi_service_api:dest_list(),
dest_list_allow
:: cloudi_service_api:dest_list(),
count_process
:: pos_integer(),
count_thread
:: pos_integer(),
max_r
:: non_neg_integer(),
max_t
:: cloudi_service_api:seconds(),
options
:: #config_service_options{},
uuid
:: cloudi_service_api:service_id()
}).
% service update plan
-record(config_service_update,
{
% service update plan configuration
type = undefined
:: undefined | internal | external,
% internal service update configuration
% internal service module to update
module = undefined
:: atom(),
% internal service state update
% equivalent to Module:code_change/3 with an Erlang/OTP behaviour
% but with the addition of the new module version and without the
% Extra variable (due to the function existing only for the upgrade).
module_state = undefined
:: undefined |
fun((OldModuleVerson :: cloudi_service_api:module_version(),
NewModuleVerson :: cloudi_service_api:module_version(),
OldState :: any()) ->
{ok, NewState :: any()} | {error, Reason :: any()} | any()),
% external service update configuration
% external service executable to use after the update
file_path = undefined
:: undefined | file:filename(),
% external service executable command-line arguments for the update
args = undefined
:: undefined | string(),
% external service executable environment variables for the update
env = undefined
:: undefined | list({string(), string()}),
% common update configuration
% should the update be done synchronously among all service processes
% (i.e., should the update operation be coordinated to not occur
% while a service request is being handled)
sync = true
:: boolean(),
% additional modules to load before module_state is called, if provided
modules_load = []
:: list(atom()),
% modules to unload after module_state is called, if provided
modules_unload = []
:: list(atom()),
% code paths to add before modules are loaded
code_paths_add = []
:: list(string()),
% code paths to remove after the update is successful
code_paths_remove = []
:: list(string()),
% destination refresh method
dest_refresh = undefined
:: undefined | cloudi_service_api:dest_refresh(),
% service initialization timeout
timeout_init = undefined
:: undefined |
cloudi_service_api:timeout_initialize_value_milliseconds(),
% default async timeout
timeout_async = undefined
:: undefined |
cloudi_service_api:timeout_send_async_value_milliseconds(),
% default sync timeout
timeout_sync = undefined
:: undefined |
cloudi_service_api:timeout_send_sync_value_milliseconds(),
% destinations denied
dest_list_deny = invalid
:: invalid | cloudi_service_api:dest_list(),
% destinations allowed
dest_list_allow = invalid
:: invalid | cloudi_service_api:dest_list(),
% service configuration option keys to update
options_keys = []
:: list(atom()),
% service configuration options to update
options = []
:: [] | #config_service_options{},
% service update plan state
% service ids of affected services
% (internal services must have all usage of the
% service module represented in this list while
% external services always have a list of length 1)
uuids = []
:: list(cloudi_service_api:service_id()),
% old module version passed to module_state function
% (internal services only)
module_version_old = undefined
:: undefined | cloudi_service_api:module_version(),
% should auto-reloading of the module be stopped during the update?
% (internal services only)
reload_stop = false
:: boolean(),
% should a new OS process be created?
% (external services only)
spawn_os_process = false
:: boolean(),
% is the service busy handling a service request?
% (when sync == true, delay module loading until after
% all service requests currently being handled are done)
update_pending = undefined
:: undefined | pid(),
% should the update occur now
% (when sync == false, delay the module_state update until after
% a service request currently being handled is done in the process)
update_now = undefined
:: undefined | pid(),
% can the update start as anticipated?
% (false if processes died during the update attempt)
update_start = true
:: boolean(),
% is a service request currently being handled?
queue_requests = undefined
:: undefined | boolean()
}).
-record(config_nodes_discovery,
{
% nodefinder interface
mode :: multicast | ec2,
module :: module(),
start_f :: atom(),
start_a :: list(),
discover_f :: atom(),
discover_a :: list(),
stop_f :: atom(),
stop_a :: list()
}).
-record(config_nodes,
{
nodes = []
:: list(node()),
% time to wait before the first reconnect is attempted with a node
reconnect_start = ?DEFAULT_NODE_RECONNECT_START
:: cloudi_service_api:node_reconnect_delay_seconds(),
% maximum wait time before a reconnect is attempted with a node
reconnect_delay = ?DEFAULT_NODE_RECONNECT_DELAY
:: cloudi_service_api:node_reconnect_delay_seconds(),
% how should node connections be monitored
listen = visible
:: visible | all,
% how should node connections be created
connect = visible
:: visible | hidden,
% what timestamp is used for generating service request transaction ids
timestamp_type = erlang
:: erlang | os | warp,
% discovery format (with defaults) is:
%
% [{multicast,
% [{address, {224,0,0,1}},
% {port, 4475},
% {ttl, 1}]}]
%
% (or)
%
% [{ec2,
% [{access_key_id, undefined},
% {secret_access_key, undefined},
% {ec2_host, "ec2.amazonaws.com"},
% {groups, []},
% {tags, []}]}]
%
discovery = undefined
:: #config_nodes_discovery{} | undefined
}).
-record(config,
{
uuid_generator
:: uuid:state(),
logging = #config_logging{}
:: #config_logging{},
acl = #{}
:: #{atom() := nonempty_list(cloudi:service_name_pattern())},
services = []
:: list(#config_service_internal{} |
#config_service_external{}),
nodes = #config_nodes{}
:: #config_nodes{}
}).