<!-- Module User's Guide -->

<chapter>

	<title>&adminguide;</title>

	<section id="overview" xreflabel="Overview">
	<title>Overview</title>
	<para>
		A SIP user location implementation. Its main purpose is to store,
		manage and provide access to SIP registration bindings (contacts) for
		other modules (e.g. registrar, mid-registrar, nathelper, etc.). The
		module exports no functions that could be directly used from the
		OpenSIPS script.
	</para>
	<para>
		At runtime, the contacts may reside in memory, in an SQL database or in
		a NoSQL database. Combinations of two of the above are also possible.
		For example, contacts may only be directly manipulated in memory in
		order to guarantee fast interactions while being asynchronously
		synchronized to an SQL database. The latter helps achieve restart
		persistency. Consult the
		<emphasis role='bold'><xref linkend="param_working_mode_preset"/></emphasis>
		parameter for more details on all possible runtime behaviors of the
		module.
	</para>
	<para>
		The OpenSIPS user location implementation is cluster-enabled. On top of
		supporting traditional "single instance" setups, it also allows multiple
		OpenSIPS user location nodes to form a single, global user location cluster.
		This allows high-level features such as startup synchronization (data
		tunneling) from a random, healthy "donor" node and evenly distributed
		NAT pinging workloads.
	</para>
	</section>

	<section id="distributed-sip-user-location"
			 xreflabel="Distributed SIP User Location">
	<title>Distributed SIP User Location</title>
	<para>
	Starting with OpenSIPS 2.4, the user location module offers several optional
	data distribution models, each tailoring to specific real-life production use cases.
	Built on top of the OpenSIPS clustering module, these models take into
	account service concerns such as <emphasis>high availability, geographical
	distribution, horizontal scalability and NAT traversal</emphasis>.
	</para>
	<para>
	Depending on data locality, the distribution models are split in two main
	categories:
	</para>
	<section id="federation-topology" xreflabel="Federation Topology">
		<title>"Federation" Topology</title>
		<para>
		A <emphasis>federated</emphasis> user location keeps contact data local
		to the original OpenSIPS node the contact initially registered to. In
		order to share the reachability of these contacts with the global
		OpenSIPS user location cluster, registrar nodes will only publish some
		light "metadata" entries for any new Addresses-of-Record which are
		reachable from them. These entries will cause other nodes to also fork
		additional SIP branches pointing to the publisher registrar upon
		receiving calls for its advertised Addresses-of-Record.
		</para>
		<para>
		The <emphasis role='bold'>federation</emphasis> topology is an
		optimized solution for the following core problems:
		<itemizedlist>
			<listitem>
			<para>
			<emphasis role='bold'>IP address restrictions</emphasis> - In some
			cases, calls routed towards registered contacts must necessarily
			pass through the original registration nodes of these contacts. A
			classic example of this situation is when an OpenSIPS registrar
			sitting at the edge of the platform is directly facing a NAT device
			on the way to the contact. Unless calls are sent out from this
			exact registrar, they will not be able to traverse the NAT device
			and reach the contact.
			</para>
			</listitem>
			<listitem>
			<para>
			<emphasis role='bold'>horizontal scalability</emphasis> - Avoiding
			global replication/contact broadcasting within the cluster not only
			dramatically improves contact storage performance, but also leads
			to better service scalability. Different geographical locations can
			be sized according to their local subscriber populations (traffic
			may be balanced to them using DNS SRV weights, for example),
			without losing platform-wide reachability.
			</para>
			</listitem>
		</itemizedlist>
		</para>
		<para>
		Currently, the metadata information may be published to NoSQL databases
		which support key/multi-value column-like associations. Example known
		backends to support these abstractions at the time of writing are
		MongoDB and Cassandra.
		</para>
		<para>
		The <ulink url="https://opensips.org/Documentation/Tutorials-Distributed-User-Location-Federation">
			<citetitle>federated user location tutorial</citetitle></ulink>
		contains precise details on how to achieve this setup (including High
		Availability support).
		</para>
	</section>
	<section id="full-sharing-topology" xreflabel="Full Sharing Topology">
		<title>"Full Sharing" Topology</title>
		<para>
		A <emphasis>fully sharing</emphasis> user location broadcasts contact
		information to all data nodes (OpenSIPS or NoSQL).
		The main assumption behind this mode is that any routing
		restrictions have been alleviated beforehand. Consequently, either SIP
		traffic egressing from a "full sharing"
		OpenSIPS user location topology is being intermediated by an
		additional SIP edge endpoint of our platform, or there are no egress IP
		restrictions at all (for example, if all SIP UAs have public IPs). In
		this setup, all OpenSIPS user location nodes are
		<emphasis>equivalent</emphasis> to one another, as they each have
		access to the same dataset and have no routing restrictions.
		</para>
		<para>
		The <emphasis role='bold'>full sharing</emphasis> topology is
		an appropriate solution for multi-layer VoIP platforms, where the
		OpenSIPS registrar nodes do not directly interact with external SIP
		endpoints. Moreover, it can be configured to fully store contact data
		within a NoSQL cluster (zero in-memory storage), thus taking full
		advantage of the data sharing, sharding, migration and other
		capabilities of a specialized distributed data handling engine.
		</para>
		<para>
		Additionally, a "full sharing" topology can be used to achieve a basic
		"hot backup" high-availability setup with an active-passive registrar
		nodes configuration, both of which make use of a shared virtual IP.
		</para>
		<para>
		Registrations may optionally be fully managed inside NoSQL
		databases which support key/multi-value column-like associations.
		Example known backends to currently support these abstractions are MongoDB
		and Apache Cassandra.
		</para>
		<para>
		The <ulink url="https://opensips.org/Documentation/Tutorials-Distributed-User-Location-Full-Sharing">
			<citetitle>"full sharing" user location tutorial</citetitle></ulink>
		contains precise details on how to achieve this setup (including full
		NoSQL storage support).
		</para>
	</section>
	<section id="n-contact-pings-problem" xreflabel="N contact pings problem">
		<title>"N Contact Pings" Problem</title>
		<para>
		A long-standing problem caused by contact information being replicated
		to multiple SIP registrar instances directly through replication or
		indirectly through a globally reachable database. As long as
		traditionally clusterized nodes are not aware of
		each other, they will each scan the entire contact dataset, thus
		periodically sending "N pings" instead of "1 ping" for each contact.
		This difference directly affects service scalability, as well as the
		amount of consumed resources such as CPU and network
		bandwidth, both on the service and client side.
		</para>
		<para>
		This problem is solved with the help of the OpenSIPS cluster layer,
		which makes all nodes aware of each others' presence. Thus, the
		distributed user location node topologies are able to collectively
		partition the pinging workload and spread it evenly across the current
		number of cluster nodes, at any given point in time.  The
		<xref linkend="param_pinging_mode"/> module parameter describes the
		built-in pinging heuristics in more detail.
		</para>
	</section>
	</section>

	<section id="contact-matching" xreflabel="Contact Matching">
	<title>Contact matching</title>
	<para>
	Contact matching (for the same Address-of-Record, AoR) is an important
	aspect of a SIP user location service, especially in the context of NAT
	traversal. The latter raises more problems, since contacts from different
	phones of same users may overlap (if behind NATs with identical
	configurations) or the re-register Contact of the same SIP User Agent may
	be seen as a new one (due to the request arriving via a new NAT binding).
	</para>
	<para>
	The SIP RFC 3261 publishes a matching algorithm based only on the
	contact string with Call-ID and CSeq number extra checking (if the Call-ID
	matches, it must have a higher CSeq number, otherwise the registration is
	invalid). But as argumented above, this is not enough in a NAT traversal
	context, so the &osips; implementation of contact matching offers more
	algorithms:
	</para>
	<itemizedlist>
		<listitem>
		<para>
			<emphasis>Contact based only</emphasis> - strict RFC 3261
			compliancy - the contact is matched as string and extra checked
			via Call-ID and CSeq (if Call-ID is the same, it must have a
			higher CSeq number, otherwise the registration is invalid).
		</para>
		</listitem>
		<listitem>
		<para>
			<emphasis>Contact and Call-ID based</emphasis> - an extension
			of the first case - the Contact and Call-ID header field values
			must match as strings; the CSeq must be higher than the previous
			one - so be careful how you deal with REGISTER retransmissions in
			this case.
		</para>
		</listitem>
	</itemizedlist>
	<para>
	For more details on how to control/select the contact matching algorithm,
	please go to
	<emphasis role='bold'><xref linkend="param_matching_mode"/></emphasis>.
	</para>
	</section>

	<section id="dependencies" xreflabel="Dependencies">
	<title>Dependencies</title>
	<section>
		<title>&osips; Modules</title>
		<para>
		The following modules must be loaded before this module:
			<itemizedlist>
			<listitem>
			<para>
				<emphasis>Optionally an SQL database module</emphasis>.
			</para>
			</listitem>
			<listitem>
			<para>
				<emphasis>Optionally a NoSQL database module</emphasis>.
			</para>
			</listitem>
			<listitem>
			<para>
				<emphasis>clusterer, if <xref linkend="param_cluster_mode"/>
				is different than "none".</emphasis>
			</para>
			</listitem>
			</itemizedlist>
		</para>
	</section>
	<section>
		<title>External Libraries or Applications</title>
		<para>
		The following libraries or applications must be installed before
		running &osips; with this module loaded:
			<itemizedlist>
			<listitem>
			<para>
				<emphasis>None</emphasis>.
			</para>
			</listitem>
			</itemizedlist>
		</para>
	</section>
	</section>
	<section id="exported_parameters" xreflabel="Exported Parameters">
	<title>Exported Parameters</title>
	<section id="param_nat_bflag" xreflabel="nat_bflag">
		<title><varname>nat_bflag</varname> (string)</title>
		<para>
		The name of the branch flag to be used as NAT marker (if the contact
		is or not natted). This is a branch flag and it will be imported and
		used by all other modules depending on the usrloc module.
		</para>
		<para>
		<emphasis>
			Default value is NULL (not set).
		</emphasis>
		</para>
		<example>
		<title>Set <varname>nat_bflag</varname> parameter</title>
		<programlisting format="linespecific">
...
modparam("usrloc", "nat_bflag", "NAT_BFLAG")
...
</programlisting>
		</example>
	</section>

	<section id="param_contact_id_column" xreflabel="contact_id_column">
		<title><varname>contact_id_column</varname> (string)</title>
		<para>
		Name of the column holding the unique contact IDs.
		</para>
		<para>
		<emphasis>
			Default value is <quote>contact_id</quote>.
		</emphasis>
		</para>
		<example>
		<title>Set <varname>contact_id_column</varname> parameter</title>
		<programlisting format="linespecific">
...
modparam("usrloc", "contact_id_column", "ctid")
...
</programlisting>
		</example>
	</section>

	<section id="param_user_column" xreflabel="user_column">
		<title><varname>user_column</varname> (string)</title>
		<para>
		Name of column containing usernames.
		</para>
		<para>
		<emphasis>
			Default value is <quote>username</quote>.
		</emphasis>
		</para>
		<example>
		<title>Set <varname>user_column</varname> parameter</title>
		<programlisting format="linespecific">
...
modparam("usrloc", "user_column", "username")
...
</programlisting>
		</example>
	</section>

	<section id="param_domain_column" xreflabel="domain_column">
		<title><varname>domain_column</varname> (string)</title>
		<para>
		Name of column containing domains.
		</para>
		<para>
		<emphasis>
			Default value is <quote>domain</quote>.
		</emphasis>
		</para>
		<example>
		<title>Set <varname>user_column</varname> parameter</title>
		<programlisting format="linespecific">
...
modparam("usrloc", "domain_column", "domain")
...
</programlisting>
		</example>
	</section>

	<section id="param_contact_column" xreflabel="contact_column">
		<title><varname>contact_column</varname> (string)</title>
		<para>
		Name of column containing contacts.
		</para>
		<para>
		<emphasis>
			Default value is <quote>contact</quote>.
		</emphasis>
		</para>
		<example>
		<title>Set <varname>contact_column</varname> parameter</title>
		<programlisting format="linespecific">
...
modparam("usrloc", "contact_column", "contact")
...
</programlisting>
		</example>
	</section>

	<section id="param_expires_column" xreflabel="expires_column">
		<title><varname>expires_column</varname> (string)</title>
		<para>
		Name of column containing expires value.
		</para>
		<para>
		<emphasis>
			Default value is <quote>expires</quote>.
		</emphasis>
		</para>
		<example>
		<title>Set <varname>expires_column</varname> parameter</title>
		<programlisting format="linespecific">
...
modparam("usrloc", "expires_column", "expires")
...
</programlisting>
		</example>
	</section>

	<section id="param_q_column" xreflabel="q_column">
		<title><varname>q_column</varname> (string)</title>
		<para>
		Name of column containing q values.
		</para>
		<para>
		<emphasis>
			Default value is <quote>q</quote>.
		</emphasis>
		</para>
		<example>
		<title>Set <varname>q_column</varname> parameter</title>
		<programlisting format="linespecific">
...
modparam("usrloc", "q_column", "q")
...
</programlisting>
		</example>
	</section>

	<section id="param_callid_column" xreflabel="callid_column">
		<title><varname>callid_column</varname> (string)</title>
		<para>
		Name of column containing callid values.
		</para>
		<para>
		<emphasis>
			Default value is <quote>callid</quote>.
		</emphasis>
		</para>
		<example>
		<title>Set <varname>callid_column</varname> parameter</title>
		<programlisting format="linespecific">
...
modparam("usrloc", "callid_column", "callid")
...
</programlisting>
		</example>
	</section>

	<section id="param_cseq_column" xreflabel="cseq_column">
		<title><varname>cseq_column</varname> (string)</title>
		<para>
		Name of column containing cseq numbers.
		</para>
		<para>
		<emphasis>
			Default value is <quote>cseq</quote>.
		</emphasis>
		</para>
		<example>
		<title>Set <varname>cseq_column</varname> parameter</title>
		<programlisting format="linespecific">
...
modparam("usrloc", "cseq_column", "cseq")
...
</programlisting>
		</example>
	</section>

	<section id="param_methods_column" xreflabel="methods_column">
		<title><varname>methods_column</varname> (string)</title>
		<para>
		Name of column containing supported methods.
		</para>
		<para>
		<emphasis>
			Default value is <quote>methods</quote>.
		</emphasis>
		</para>
		<example>
		<title>Set <varname>methods_column</varname> parameter</title>
		<programlisting format="linespecific">
...
modparam("usrloc", "methods_column", "methods")
...
</programlisting>
		</example>
	</section>

	<section id="param_flags_column" xreflabel="flags_column">
		<title><varname>flags_column</varname> (string)</title>
		<para>
		Name of column to save the internal flags of the record.
		</para>
		<para>
		<emphasis>
			Default value is <quote>flags</quote>.
		</emphasis>
		</para>
		<example>
		<title>Set <varname>flags_column</varname> parameter</title>
		<programlisting format="linespecific">
...
modparam("usrloc", "flags_column", "flags")
...
</programlisting>
		</example>
	</section>

	<section id="param_cflags_column" xreflabel="cflags_column">
		<title><varname>cflags_column</varname> (string)</title>
		<para>
		Name of column to save the branch/contact flags of the record.
		</para>
		<para>
		<emphasis>
			Default value is <quote>cflags</quote>.
		</emphasis>
		</para>
		<example>
		<title>Set <varname>cflags_column</varname> parameter</title>
		<programlisting format="linespecific">
...
modparam("usrloc", "cflags_column", "cflags")
...
</programlisting>
		</example>
	</section>

	<section id="param_user_agent_column" xreflabel="user_agent_column">
		<title><varname>user_agent_column</varname> (string)</title>
		<para>
		Name of column containing user-agent values.
		</para>
		<para>
		<emphasis>
			Default value is <quote>user_agent</quote>.
		</emphasis>
		</para>
		<example>
		<title>Set <varname>user_agent_column</varname> parameter</title>
		<programlisting format="linespecific">
...
modparam("usrloc", "user_agent_column", "user_agent")
...
</programlisting>
		</example>
	</section>

	<section id="param_received_column" xreflabel="received_column">
		<title><varname>received_column</varname> (string)</title>
		<para>
		Name of column containing the source IP, port, and protocol from the REGISTER
		message.
		</para>
		<para>
		<emphasis>
			Default value is <quote>received</quote>.
		</emphasis>
		</para>
		<example>
		<title>Set <varname>received_column</varname> parameter</title>
		<programlisting format="linespecific">
...
modparam("usrloc", "received_column", "received")
...
</programlisting>
		</example>
	</section>

	<section id="param_socket_column" xreflabel="socket_column">
		<title><varname>socket_column</varname> (string)</title>
		<para>
		Name of column containing the received socket information (IP:port)
		for the REGISTER message.
		</para>
		<para>
		<emphasis>
			Default value is <quote>socket</quote>.
		</emphasis>
		</para>
		<example>
		<title>Set <varname>socket_column</varname> parameter</title>
		<programlisting format="linespecific">
...
modparam("usrloc", "socket_column", "socket")
...
</programlisting>
		</example>
	</section>

	<section id="param_path_column" xreflabel="path_column">
		<title><varname>path_column</varname> (string)</title>
		<para>
		Name of column containing the Path header.
		</para>
		<para>
		<emphasis>
			Default value is <quote>path</quote>.
		</emphasis>
		</para>
		<example>
		<title>Set <varname>path_column</varname> parameter</title>
		<programlisting format="linespecific">
...
modparam("usrloc", "path_column", "path")
...
</programlisting>
		</example>
	</section>

	<section id="param_sip_instance_column" xreflabel="sip_instance_column">
		<title><varname>sip_instance_column</varname> (string)</title>
		<para>
		Name of column containing the SIP instance.
		</para>
		<para>
		<emphasis>
			Default value is <quote>NULL</quote>.
		</emphasis>
		</para>
		<example>
		<title>Set <varname>sip_instance_column</varname> parameter</title>
		<programlisting format="linespecific">
...
modparam("usrloc", "sip_instance_column", "sip_instance")
...
</programlisting>
		</example>
	</section>

	<section id="param_kv_store_column" xreflabel="kv_store_column">
		<title><varname>kv_store_column</varname> (string)</title>
		<para>
		Name of column containing generic key-value data.
		</para>
		<para>
		<emphasis>
			Default value is <quote>kv_store</quote>.
		</emphasis>
		</para>
		<example>
		<title>Set <varname>kv_store_column</varname> parameter</title>
		<programlisting format="linespecific">
...
modparam("usrloc", "kv_store_column", "json_data")
...
</programlisting>
		</example>
	</section>

	<section id="param_attr_column" xreflabel="attr_column">
		<title><varname>attr_column</varname> (string)</title>
		<para>
		Name of column containing additional registration-related information.
		</para>
		<para>
		<emphasis>
			Default value is <quote>attr</quote>.
		</emphasis>
		</para>
		<example>
		<title>Set <varname>attr_column</varname> parameter</title>
		<programlisting format="linespecific">
...
modparam("usrloc", "attr_column", "attributes")
...
</programlisting>
		</example>
	</section>

	<section id="param_use_domain" xreflabel="use_domain">
		<title><varname>use_domain</varname> (integer)</title>
		<para>
		If the domain part of the user should be also saved and used for
		identifing the user (along with the username part). Useful in
		multi domain scenarios. Non 0 value means true.
		</para>
		<para>
		<emphasis>
			Default value is <quote>0 (false)</quote>.
		</emphasis>
		</para>
		<example>
		<title>Set <varname>use_domain</varname> parameter</title>
		<programlisting format="linespecific">
...
modparam("usrloc", "use_domain", 1)
...
</programlisting>
		</example>
	</section>

	<section id="param_desc_time_order" xreflabel="desc_time_order">
		<title><varname>desc_time_order</varname> (integer)</title>
		<para>
		If the user's contacts should be kept timestamp ordered; otherwise the
		contact will be ordered based on q value.
		Non 0 value means true.
		</para>
		<para>
		<emphasis>
			Default value is <quote>0 (false)</quote>.
		</emphasis>
		</para>
		<example>
		<title>Set <varname>desc_time_order</varname> parameter</title>
		<programlisting format="linespecific">
...
modparam("usrloc", "desc_time_order", 1)
...
</programlisting>
		</example>
	</section>

	<section id="param_timer_interval" xreflabel="timer_interval">
		<title><varname>timer_interval</varname> (integer)</title>
		<para>
		Number of seconds between two timer runs.  During each run, the module
		will update/delete dirty/expired contacts from memory and/or mirror
		these operations to the database, if configured to do so.
		</para>
		<warning>
		<para>
		In case of an OpenSIPS shutdown or even a crash, contacts which are in
		memory only and have not been flushed yet to disk will NOT get lost!
		OpenSIPS will try its best to do a last-minute sync to DB right before
		shutting down.
		</para>
		</warning>
		<para>
		<emphasis>
			Default value is 60.
		</emphasis>
		</para>
		<example>
		<title>Set <varname>timer_interval</varname> parameter</title>
		<programlisting format="linespecific">
...
modparam("usrloc", "timer_interval", 120)
...
</programlisting>
		</example>
	</section>

	<section id="param_db_url" xreflabel="db_url">
		<title><varname>db_url</varname> (string)</title>
		<para>
		&url; of the database that should be used.
		</para>
		<para>
		<emphasis>
			Default value is <quote>&defaultdb;</quote>.
		</emphasis>
		</para>
		<example>
		<title>Set <varname>db_url</varname> parameter</title>
		<programlisting format="linespecific">
...
modparam("usrloc", "db_url", "&exampledb;")
...
</programlisting>
		</example>
	</section>

	<section id="param_cachedb_url" xreflabel="cachedb_url">
		<title><varname>cachedb_url</varname> (string)</title>
		<para>
		&url; of a NoSQL database to be used. Only required in a
		cachedb-enabled
		<emphasis role='bold'><xref linkend="param_cluster_mode"/></emphasis>.
		</para>
		<para>
		<emphasis>
			Default value is <quote>none</quote>.
		</emphasis>
		</para>
		<example>
		<title>Set <varname>cachedb_url</varname> parameter</title>
		<programlisting format="linespecific">
...
modparam("usrloc", "cachedb_url", "&examplecdb;")
...
</programlisting>
		</example>
	</section>

	<section id="param_db_mode" xreflabel="db_mode">
		<title><varname>db_mode</varname> (integer, deprecated)</title>
		<para>
		This parameter has been kept for backwards compatibility.  It acts as a
		<xref linkend="param_working_mode_preset"/> (which it also conflicts with),
		overriding any <xref linkend="param_cluster_mode"/>,
		<xref linkend="param_restart_persistency"/> and
		<xref linkend="param_sql_write_mode"/> settings.  Possible values are:
		</para>
		<para>
		<itemizedlist>
		<listitem>
			<para>0, corresponding to "single-instance-no-db" (see below)</para>
		</listitem>
		<listitem>
			<para>1, corresponding to "single-instance-sql-write-through"</para>
		</listitem>
		<listitem>
			<para>2, corresponding to "single-instance-sql-write-back"</para>
		</listitem>
		<listitem>
			<para>3, corresponding to "sql-only"</para>
		</listitem>
		</itemizedlist>
		</para>
		<para>
		<emphasis>
			Default value is "not set".
		</emphasis>
		</para>
		<example>
		<title>Set <varname>db_mode</varname> parameter</title>
		<programlisting format="linespecific">
...
modparam("usrloc", "db_mode", 2)
...
</programlisting>
		</example>
	</section>

	<section id="param_working_mode_preset" xreflabel="working_mode_preset">
		<title><varname>working_mode_preset</varname> (string)</title>
		<para>
		A pre-defined working mode for the usrloc module.  Setting this
		parameter will override any <xref linkend="param_cluster_mode"/>,
		<xref linkend="param_restart_persistency"/> and
		<xref linkend="param_sql_write_mode"/> settings.
		</para>
		<para>
		<itemizedlist>
		<listitem>
			<para>
			<emphasis role="bold">"single-instance-no-db"</emphasis> - This
			disables database completely. Only memory will be used.
			Contacts will not survive restart. Use this value if you need a
			really fast usrloc and contact persistence is not necessary or
			is provided by other means.
			</para>
		</listitem>
		<listitem>
			<para>
			<emphasis role="bold">"single-instance-sql-write-through"</emphasis>
			- Write-Through scheme. All changes to usrloc are immediately
			reflected in database too. This is very slow, but very reliable.
			Use this scheme if speed is not your priority but need to make
			sure that no registered contacts will be lost during crash or
			reboot.
			</para>
		</listitem>
		<listitem>
			<para>
			<emphasis role="bold">"single-instance-sql-write-back"</emphasis>
			- Write-Back scheme. This is a combination of previous two
			schemes. All changes are made to memory and database
			synchronization is done in the timer. The timer deletes all
			expired contacts and flushes all modified or new contacts to
			database.  Use this scheme if you encounter high-load peaks
			and want them to process as fast as possible. The mode will
			not help at all if the load is high all the time.  The
			added latency on the SIP signaling when using this asynchronous
			preset is much lower than the one added by the safe but
			blocking, "single-instance-sql-write-through" preset.
			</para>
		</listitem>
		<listitem>
			<para>
			<emphasis role="bold">"sql-only"</emphasis> -
			DB-Only scheme. No memory cache is kept, all operations being
			directly performed with the database. The timer deletes all
			expired contacts from database - cleans after clients that didn't
			un-register or re-register. The mode is useful if you configure
			more servers sharing the same DB without any replication at SIP
			level. The mode may be slower due the high number of DB operation.
			For example NAT pinging is a killer since during each ping cycle
			all nated contact are loaded from the DB; The lack of memory
			caching also disable the statistics exports.
			</para>
		</listitem>
		<listitem>
			<para>
			<emphasis role="bold">"federation-cachedb-cluster"</emphasis> -
			OpenSIPS will run with a "federation-cachedb"
			<xref linkend="param_cluster_mode"/> and
			"sync-from-cluster" <xref linkend="param_restart_persistency"/>.
			This will require the configuration of multiple "seed" nodes in
			the cluster. Refer to the <ulink url="https://opensips.org/Documentation/Tutorials-Distributed-User-Location-Federation">
			<citetitle>federated user location tutorial</citetitle></ulink> for more
			details.
			</para>
		</listitem>
		<listitem>
			<para>
			<emphasis role="bold">"full-sharing-cluster"</emphasis> -
			OpenSIPS will run with a "full-sharing"
			<xref linkend="param_cluster_mode"/> and
			"sync-from-cluster" <xref linkend="param_restart_persistency"/>.
			This will require the configuration of one of the nodes in the cluster
			as a "seed" node in order to bootstrap the syncing process.
			</para>
		</listitem>
		<listitem>
			<para>
			<emphasis role="bold">"full-sharing-cachedb-cluster"</emphasis> -
			OpenSIPS will run with a "full-sharing-cachedb"
			<xref linkend="param_cluster_mode"/>, where all location data strictly
			resides in a NoSQL database, thus it will have natural restart
			persistency.
			</para>
		</listitem>
		</itemizedlist>
		</para>
		<para>
		Refer to section
		<xref linkend="distributed-sip-user-location"/> for details
		regarding the clustering topologies and their behavior.
		</para>
		<para>
		<emphasis>
			Default value is "single-instance-no-db".
		</emphasis>
		</para>
		<example>
		<title>Set <varname>working_mode_preset</varname> parameter</title>
		<programlisting format="linespecific">
...
modparam("usrloc", "working_mode_preset", "full-sharing-cachedb-cluster")
...
</programlisting>
		</example>
	</section>

	<section id="param_cluster_mode" xreflabel="cluster_mode">
		<title><varname>cluster_mode</varname> (string)</title>
		<para>
			<emphasis role='bold'>This parameter will get overridden if either
			<xref linkend='param_working_mode_preset'/> or
			<xref linkend='param_db_mode'/> is set.</emphasis>
		</para>
		<para>
		The behavior of the global OpenSIPS user location cluster. Refer to
		section <xref linkend="distributed-sip-user-location"/> for details.
		</para>
		<para>
		This parameter may take the following values:
		</para>
		<itemizedlist>
			<listitem>
				<para><emphasis>"none"</emphasis> - single instance mode.
				</para>
			</listitem>
			<listitem>
				<para><emphasis>"federation-cachedb"</emphasis> -
				federation-based data sharing. Local AoR metadata is published
				inside a NoSQL database, so other cluster nodes can fork SIP
				traffic over to the current node. Consequently, the
				<xref linkend="param_location_cluster"/> and
				<xref linkend="param_cachedb_url"/> parameters are mandatory.
				</para>
			</listitem>
			<listitem>
				<para><emphasis>"full-sharing"</emphasis> -
				Broadcast contact updates (full-mesh mirroring) to all other
				OpenSIPS cluster participants.  Each node will hold the entire
				user location dataset.  Consequently, the
				<xref linkend="param_location_cluster"/> parameter is mandatory.
				</para>
			</listitem>
			<listitem>
				<para><emphasis>"full-sharing-cachedb"</emphasis> -
				Full contact data management through the use of a NoSQL
				database (somewhat resembling the "sql-only" preset).
				The cluster layer is still required in order to
				be able to partition and spread the pinging workload evenly
				among participating OpenSIPS nodes. Consequently, the
				<xref linkend="param_location_cluster"/> and
				<xref linkend="param_cachedb_url"/> parameters are mandatory.
				</para>
			</listitem>
			<listitem>
				<para><emphasis>"sql-only"</emphasis> -
				Multiple OpenSIPS boxes using a common
				<xref linkend="param_db_url"/> without necessarily being aware
				of each other.
				</para>
			</listitem>
		</itemizedlist>
		<para>
		<emphasis>
			Default value is <emphasis>"none" (single instance mode)</emphasis>.
		</emphasis>
		</para>
		<example>
		<title>Set <varname>cluster_mode</varname> parameter</title>
		<programlisting format="linespecific">
...
modparam("usrloc", "cluster_mode", "federation-cachedb")
...
</programlisting>
		</example>
	</section>

	<section id="param_restart_persistency" xreflabel="restart_persistency">
		<title><varname>restart_persistency</varname> (string)</title>
		<para>
			<emphasis role='bold'>This parameter will get overridden if either
			<xref linkend='param_working_mode_preset'/> or
			<xref linkend='param_db_mode'/> are set.</emphasis>
		</para>
		<para>
		Controls the behavior of the OpenSIPS user location following a
		restart. This parameter has no effect in some database-only working
		mode presets, where restart persistency is naturally ensured.
		</para>
		<para>
		This parameter may take the following values:
		</para>
		<itemizedlist>
			<listitem>
				<para><emphasis>"none"</emphasis> - no explicit data
				synchronization following a restart. The node starts empty.
				</para>
			</listitem>
			<listitem>
				<para><emphasis>"load-from-sql"</emphasis> - enable
				SQL-based restart persistency. This causes all runtime
				in-memory writes (i.e. new registrations, re-registrations or
				de-registrations) to also propagate to an SQL database, from
				which all data will be imported following a restart.
				Choosing this value will make the <xref linkend='param_db_url'/>
				parameter mandatory, as well as cause
				<xref linkend='param_sql_write_mode'/> to default to "write-back"
				instead of "none".
				</para>
			</listitem>
			<listitem>
				<para><emphasis>"sync-from-cluster"</emphasis> - enable
				cluster-based restart persistency. Following a restart,
				an OpenSIPS cluster node will search for a healthy "donor" node
				from which to mirror the entire user location dataset via
				direct cluster sync (TCP-based, binary-encoded data transfer).
				Depending on the clustering mode and cluster topology, this will
				require the configuration of one or multiple "seed" nodes in the cluster.
				Choosing this value will make the
				<xref linkend='param_location_cluster'/> parameter mandatory.
				</para>
			</listitem>
		</itemizedlist>
		<para>
		<emphasis>
			Default value is
			<emphasis>"none" (no restart persistency)</emphasis>.
		</emphasis>
		</para>
		<example>
		<title>Set <varname>restart_persistency</varname> parameter</title>
		<programlisting format="linespecific">
...
modparam("usrloc", "restart_persistency", "sync-from-cluster")
...
</programlisting>
		</example>
	</section>

	<section id="param_sql_write_mode" xreflabel="sql_write_mode">
		<title><varname>sql_write_mode</varname> (string)</title>
		<para>
			<emphasis role='bold'>This parameter will get overridden if either
			<xref linkend='param_working_mode_preset'/> or
			<xref linkend='param_db_mode'/> are set.</emphasis>
		</para>
		<para>
		Only valid if <xref linkend="param_restart_persistency"/> is enabled.
		Controls the runtime behavior of OpenSIPS writes to the SQL database.
		</para>
		<para>
		This parameter may take the following values:
		</para>
		<itemizedlist>
			<listitem>
				<para><emphasis>"none"</emphasis> - do not perform any
				additional SQL writes at runtime to an SQL database in order
				to specifically ensure restart persistency.
				</para>
			</listitem>
			<listitem>
				<para><emphasis>"write-through"</emphasis> - all in-memory
				writes (i.e. new registrations, re-registrations or
				de-registrations) also propagate into the SQL database, inline.
				While this will definitely slow down registration performance
				(lookups are served from memory!), it has the advantage of
				making the instance crash-safe.
				</para>
			</listitem>
			<listitem>
				<para><emphasis>"write-back"</emphasis> - all in-memory
				writes (i.e. new registrations, re-registrations or
				de-registrations) eventually also propagate into the SQL
				database, thanks to a separate timer routine. This dramatically
				speeds up registrations, but also introduces the
				possibility of crashing before the latest contact changes are
				propagated to the database. See the
				<xref linkend='param_timer_interval'/> for additional configuration.
				</para>
			</listitem>
		</itemizedlist>
		<para>
		<emphasis>
			Default value is <emphasis>"none" (no added SQL writes)</emphasis>.
		</emphasis>
		</para>
		<example>
		<title>Set <varname>sql_write_mode</varname> parameter</title>
		<programlisting format="linespecific">
...
modparam("usrloc", "sql_write_mode", "write-back")
...
</programlisting>
		</example>
	</section>

	<section id="param_matching_mode" xreflabel="matching_mode">
		<title><varname>matching_mode</varname> (integer)</title>
		<para>
		What contact matching algorithm to be used. Refer to section
		<xref linkend="contact-matching"/> for the description of the
		algorithms.
		</para>
		<para>
		The parameter may take the following values:
		</para>
		<itemizedlist>
			<listitem>
				<para><emphasis>0</emphasis> - CONTACT ONLY based matching
				algorithm.
				</para>
			</listitem>
			<listitem>
				<para><emphasis>1</emphasis> - CONTACT and CALLID based
				matching algorithm.
				</para>
			</listitem>
		</itemizedlist>
		<para>
		<emphasis>
			Default value is <emphasis>0 (CONTACT_ONLY)</emphasis>.
		</emphasis>
		</para>
		<example>
		<title>Set <varname>matching_mode</varname> parameter</title>
		<programlisting format="linespecific">
...
modparam("usrloc", "matching_mode", 1)
...
</programlisting>
		</example>
	</section>

	<section id="param_cseq_delay" xreflabel="cseq_delay">
		<title><varname>cseq_delay</varname> (integer)</title>
		<para>
		Delay (in seconds) for accepting as retransmissions register requests
		with same Call-ID and Cseq. The delay is calculated starting from the
		receiving time of the first register with that Call-ID and Cseq.
		</para>
		<para>
		Retransmissions within this delay interval will be accepted and replied
		as the original request, but no update will be done in location. If the
		delay is exceeded, error is reported.
		</para>
		<para>
		A value of 0 disable the retransmission detection.
		</para>
		<para>
		<emphasis>
			Default value is <quote>20 seconds</quote>.
		</emphasis>
		</para>
		<example>
		<title>Set <varname>cseq_delay</varname> parameter</title>
		<programlisting format="linespecific">
...
modparam("usrloc", "cseq_delay", 5)
...
</programlisting>
		</example>
	</section>

	<section id="param_location_cluster" xreflabel="location_cluster">
		<title><varname>location_cluster</varname> (integer)</title>
		<para>
		Specifies the cluster ID which this instance will send to and receive
		from all user-location related information
        (<emphasis>addresses-of-record</emphasis>, <emphasis>contacts</emphasis>),
		organized into specific events (inserts, deletes or updates).
		</para>

			&clusterer_sync_cap_para;

		<para>
		Default value is 0 (replication disabled).
		</para>
		<para>
		More details on the user location distribution mechanisms are
		available under <xref linkend="distributed-sip-user-location"/>.
		</para>
		<example>
		<title>Setting the <varname>location_cluster</varname> parameter</title>
		<programlisting format="linespecific">
...
modparam("usrloc", "location_cluster", 1)
...
</programlisting>
		</example>
	</section>

	<section id="param_skip_replicated_db_ops" xreflabel="skip_replicated_db_ops">
		<title><varname>skip_replicated_db_ops</varname> (int)</title>
		<para>
		Prevent &osips; from performing any DB-related contact operations
		when events are received over the <emphasis>Binary Interface</emphasis>.
		This is commonly used to prevent unneeded duplicate operations.
		</para>
		<para>
		Default value is "0" (upon receival of usrloc-related Binary Interface
		events, DB queries may be freely performed)
		</para>
		<para>
		More details on the user location replication mechanism are available
		in <xref linkend="distributed-sip-user-location"/>
		</para>
		<example>
		<title>Setting the <varname>skip_replicated_db_ops</varname>
			parameter</title>
		<programlisting format="linespecific">
...
modparam("usrloc", "skip_replicated_db_ops", 1)
...
</programlisting>
		</example>
	</section>

	<section id="param_max_contact_delete" xreflabel="max_contact_delete">
		<title><varname>max_contact_delete</varname> (int)</title>
		<para>
			Relevant only in WRITE_THROUGH or WRITE_BACK schemes. The maximum
		number of contacts to be deleted from the database at once. Will delete
		all of them, if fewer after passing through all the contacts.
		</para>
		<para>
		Default value is "10"
		</para>
		<example>
		<title>Setting the <varname>max_contact_delete</varname>
			parameter</title>
		<programlisting format="linespecific">
...
modparam("usrloc", "max_contact_delete", 10)
...
</programlisting>
		</example>
	</section>


	<section id="param_hash_size" xreflabel="hash_size">
		<title><varname>hash_size</varname> (integer)</title>
		<para>
		The number of entries of the hash table used by usrloc to store the
		location records is 2^hash_size. For hash_size=4, the number of entries
		of the hash table is 16. Since version 2.2, the maximu size of this
		parameter is 16, meaning that the hash supports maximum 65536 entries.
		</para>
		<para>
		<emphasis>
			Default value is <quote>9</quote>.
		</emphasis>
		</para>
		<example>
		<title>Set <varname>hash_size</varname> parameter</title>
		<programlisting format="linespecific">
...
modparam("usrloc", "hash_size", 10)
...
</programlisting>
		</example>
	</section>

	<section id="param_regen_broken_contactid" xreflabel="regen_broken_contactid">
		<title><varname>regen_broken_contactid</varname> (integer)</title>
		<para>
			Since version 2.2, <emphasis role='bold'>contact_id</emphasis> concept
		was introduced. Since this parameter validates a contact each time &osips;
		is started, there are times when the value of this parameter should be
		regenerated. That is when <emphasis role='bold'>location</emphasis> table
		is being migrated from a version older than 2.2 or when
		<emphasis role='bold'>hash_size</emphasis> module parameter is changed.
		Enabling this parameter will regenerate broken contact id's based on
		current configurations.
		</para>
		<para>
			<emphasis>
				Default value is <quote>0(not enabled)</quote>
			</emphasis>
		</para>

		<example>
		<title>Set <varname>regen_broken_contactid</varname> parameter</title>
		<programlisting format="linespecific">
...
modparam("usrloc", "regen_broken_contactid", 1)
...
</programlisting>
		</example>
	</section>

	<section id="param_latency_event_min_us" xreflabel="latency_event_min_us">
		<title><varname>latency_event_min_us</varname> (integer)</title>
		<para>
		Defines a minimal pinging latency threshold, in microseconds, past
		which contact pinging latency update events will get raised. By
		default, an event is raised for each ping reply (i.e. latency update).
		</para>
		<para>
		If both <xref linkend="param_latency_event_min_us"/> and
		<xref linkend="param_latency_event_min_us_delta"/> are set, the event
		will get raised if either of them is true.
		</para>
		<para>
			<emphasis>
				Default value is <quote>0 (no bottom limit set)</quote>.
			</emphasis>
		</para>

		<example>
		<title>Set <varname>latency_event_min_us</varname> parameter</title>
		<programlisting format="linespecific">
...
# raise an event for any 425+ ms pinging latency
modparam("usrloc", "latency_event_min_us", 425000)
...
</programlisting>
		</example>
	</section>

	<section id="param_latency_event_min_us_delta"
			 xreflabel="latency_event_min_us_delta">
		<title><varname>latency_event_min_us_delta</varname> (integer)</title>
		<para>
		Defines a minimal, absolute pinging latency difference, in
		microseconds, past which contact pinging latency update events will get
		raised. The difference is computed using the latencies of the last two
		contact pinging replies. By default, an event is raised for each ping
		reply (i.e. latency update).
		</para>
		<para>
		If both <xref linkend="param_latency_event_min_us"/> and
		<xref linkend="param_latency_event_min_us_delta"/> are set, the event
		will get raised if either of them is true.
		</para>
		<para>
			<emphasis>
				Default value is <quote>0 (no minimal latency delta set)</quote>.
			</emphasis>
		</para>

		<example>
		<title>Set <varname>latency_event_min_us_delta</varname> parameter</title>
		<programlisting format="linespecific">
...
# raise an event only if a contact has pinging latency swings of 300+ ms
modparam("usrloc", "latency_event_min_us_delta", 300000)
...
</programlisting>
		</example>
	</section>

	<section id="param_pinging_mode" xreflabel="pinging_mode">
		<title><varname>pinging_mode</varname> (string)</title>
		<para>
		Depending on the <xref linkend="param_cluster_mode"/>, the module
		can perform contact pinging using one of two possible heuristics:
		<itemizedlist>
		<listitem>
			<para><emphasis role='bold'>"ownership"</emphasis> - this instance
				will only attempt to ping a contact if it decides it is the
				logical owner of the contact.  If a shared tag is attached to
				a contact, a node will keep sending pings to that contact as
				long as it owns the respective tag.  If no shared tag has been
				specified for a given contact, the default is to assume
				permanent ownership of the contact and ping it upon request.
			</para>
		</listitem>
		<listitem>
			<para><emphasis role='bold'>"cooperation"</emphasis> - the
				assumption behind this pinging heuristic is that all
				user location cluster nodes are symmetrical (possibly
				front-ended by a SIP traffic balancing entity), such that
				<emphasis role='bold'>either </emphasis> of them can ping
				<emphasis role='bold'>any</emphasis> contact.
				Under this assumption, all currently online user location
				cluster nodes will cooperate and evenly split the pinging
				workload between them by hashing AoRs modulo
				current_number_of_online_nodes, and only picking the ones that
				they are responsible for.
			</para>
		</listitem>
		</itemizedlist>
		</para>
		<table frame='all'><title>Possible values for the "pinging_mode",
				depending on the current "cluster_mode"</title>
		<tgroup cols='6' align='left' colsep='1' rowsep='1'>
		<tbody>
		<row>
			<entry><xref linkend="param_cluster_mode"/></entry>
			<entry align="center">none</entry>
			<entry align="center">federation-cachedb</entry>
			<entry align="center">full-sharing</entry>
			<entry align="center">full-sharing-cachedb</entry>
			<entry align="center">sql-only</entry>
		</row>
		<row>
			<entry><xref linkend="param_pinging_mode"/></entry>
			<entry align="center"><emphasis role='bold'>ownership</emphasis></entry>
			<entry align="center"><emphasis role='bold'>ownership</emphasis></entry>
			<entry align="center"><emphasis role='bold'>cooperation</emphasis> / ownership</entry>
			<entry align="center"><emphasis role='bold'>cooperation</emphasis></entry>
			<entry align="center"><emphasis>unmaintained</emphasis></entry>
		</row>
		</tbody>
		</tgroup>
		</table>
		<para>
			Notice that only the <emphasis role='bold'>"full-sharing"</emphasis>
			clustering mode allows some flexibility -- all other modes are
			logically tied to a single pinging logic.  Any unaccepted value,
			according to the above table, set
			for those modes will be silently discarded.
		</para>
		<example>
		<title>Set <varname>pinging_mode</varname> parameter</title>
		<programlisting format="linespecific">
...
# prepare an active/backup "full-sharing" setup, with no front-end
modparam("usrloc", "pinging_mode", "ownership")
...
</programlisting>
		</example>
	</section>

	<section id="param_mi_dump_kv_store" xreflabel="mi_dump_kv_store">
		<title><varname>mi_dump_kv_store</varname> (integer)</title>
		<para>
		Enable in order to include the "KV-Store" field in all usrloc MI
		commands which output AoR or Contact representations.  This verbose
		field contains custom data attached to each of these two entities.
		mid_registrar makes use of both of these holders, for example.
		</para>
		<para>
			<emphasis>
				Default value is <quote>0 (disabled)</quote>.
			</emphasis>
		</para>

		<example>
		<title>Set <varname>mi_dump_kv_store</varname> parameter</title>
		<programlisting format="linespecific">
...
# include the "KV-Store" key in all usrloc MI output
modparam("usrloc", "mi_dump_kv_store", 1)
...
</programlisting>
		</example>
	</section>

	<section id="param_contact_refresh_timer" xreflabel="contact_refresh_timer">
		<title><varname>contact_refresh_timer</varname> (boolean)</title>
		<para>
		Enable a timer which will periodically scan a sorted list of contacts
		and raise the <xref linkend="event_E_UL_CONTACT_REFRESH"/> for any of
		them which are past their re-registration time interval limit.  This
		limit may given by registrar's <emphasis>pn_trigger_interval</emphasis>
		module parameter, for example.
		</para>
		<para>
			<emphasis>
				Default value is <quote>false (disabled)</quote>.
			</emphasis>
		</para>

		<example>
		<title>Set <varname>contact_refresh_timer</varname> parameter</title>
		<programlisting format="linespecific">
...
modparam("usrloc", "contact_refresh_timer", true)
...
</programlisting>
		</example>
	</section>

	</section>

	<section id="exported_functions" xreflabel="exported_functions">
	<title>Exported Functions</title>

	<section id="func_ul_add_key" xreflabel="ul_add_key(domain, aor, key_name[, key_value])">
		<title>
		<function moreinfo="none">ul_add_key(domain, aor, key_name, [key_value])</function>
		</title>
		<para>
		Append a Key/Value to the Key-Value-Store of a Usrloc-Record.
		</para>
		<para>
		Returns false, if no record is found is usrloc.
		</para>
		<para>Meaning of the parameters is as follows:</para>
		<itemizedlist>
		<listitem>
			<para><emphasis>domain (string)</emphasis> - Domain of the
			        AOR, e.g. "location"
			</para>
		</listitem>
		<listitem>
			<para><emphasis>aor (string)</emphasis> - Address-of-Record,
			        save the key for a specific (registered) user.
			</para>
		</listitem>
		<listitem>
			<para><emphasis>key (string)</emphasis> - The name of the
			        key to be stored.
			</para>
		</listitem>
		<listitem>
			<para><emphasis>value (string, optional)</emphasis>
			        - The value to be stored. Not providing the value or
			        by providing an empty value, will delete the entry.
			</para>
		</listitem>
		</itemizedlist>
		<para>
		This function can be used in ANY route.
		</para>
		<example>
		<title><function>ul_add_key</function> usage</title>
		<programlisting format="linespecific">
...
ul_add_key("location", "$tU@$td", "service_route", "$hdr(Service-Route)");
...
</programlisting>
		</example>
	</section>

	<section id="func_ul_get_key" xreflabel="ul_get_key(domain, aor, key_name, destination)">
		<title>
		<function moreinfo="none">ul_get_key(domain, aor, key_name, destination)</function>
		</title>
		<para>
		Retrieve a Key/Value from the Key-Value-Store of a Usrloc-Record.
		</para>
		<para>
		Returns false, if no record is found is usrloc or no according key is found.
		</para>
		<para>Meaning of the parameters is as follows:</para>
		<itemizedlist>
		<listitem>
			<para><emphasis>domain (string)</emphasis> - Domain of the
			        AOR, e.g. "location"
			</para>
		</listitem>
		<listitem>
			<para><emphasis>aor (string)</emphasis> - Address-of-Record,
			        save the key for a specific (registered) user.
			</para>
		</listitem>
		<listitem>
			<para><emphasis>key (string)</emphasis> - The name of the
			        key to be retrieved.
			</para>
		</listitem>
		<listitem>
			<para><emphasis>destination (variable)</emphasis>
			        - A variable, where to store the retrieved key.
			</para>
		</listitem>
		</itemizedlist>
		<para>
		This function can be used in ANY route.
		</para>
		<example>
		<title><function>ul_get_key</function> usage</title>
		<programlisting format="linespecific">
...
if (ul_get_key("location", "$tU@$td", "service_route", $avp(service_route))) {
        append_to_reply("Service-Route: $avp(service_route)\r\n");
}
...
</programlisting>
		</example>
	</section>

	<section id="func_ul_del_key" xreflabel="ul_del_key(domain, aor, key_name)">
		<title>
		<function moreinfo="none">ul_del_key(domain, aor, key_name)</function>
		</title>
		<para>
		Deletes a Key/Value from the Key-Value-Store of a Usrloc-Record.
		</para>
		<para>
		Returns false, if no record is found is usrloc.
		</para>
		<para>Meaning of the parameters is as follows:</para>
		<itemizedlist>
		<listitem>
			<para><emphasis>domain (string)</emphasis> - Domain of the
			        AOR, e.g. "location"
			</para>
		</listitem>
		<listitem>
			<para><emphasis>aor (string)</emphasis> - Address-of-Record,
			        save the key for a specific (registered) user.
			</para>
		</listitem>
		<listitem>
			<para><emphasis>key (string)</emphasis> - The name of the
			        key to be deleted.
			</para>
		</listitem>
		</itemizedlist>
		<para>
		This function can be used in ANY route.
		</para>
		<example>
		<title><function>ul_del_key</function> usage</title>
		<programlisting format="linespecific">
...
ul_del_key("location", "$tU@$td", "service_route");
...
</programlisting>
		</example>
	</section>

	</section>


	<section id="exported_mi_functions" xreflabel="Exported MI Functions">
	<title>Exported MI Functions</title>

	<section id="mi_ul_rm" xreflabel="ul_rm">
		<title>
		<function moreinfo="none">ul_rm</function>
		</title>
		<para>
		Deletes an entire AOR record (including its contacts).
		</para>
		<para>Parameters: </para>
		<itemizedlist>
			<listitem><para>
				<emphasis>table_name</emphasis> - table where the AOR
				is removed from (Ex: location).
			</para></listitem>
			<listitem><para>
				<emphasis>aor</emphasis> - user AOR in username[@domain]
				format (domain must be supplied only if use_domain option
				is on).
			</para></listitem>
		</itemizedlist>
	</section>

	<section id="mi_ul_rm_contact" xreflabel="ul_rm_contact">
		<title>
		<function moreinfo="none">ul_rm_contact</function>
		</title>
		<para>
		Deletes a contact from an AOR record.
		</para>
		<para>Parameters: </para>
		<itemizedlist>
			<listitem><para>
				<emphasis>table name</emphasis> - table where the AOR
				is removed from (Ex: location).
			</para></listitem>
			<listitem><para>
				<emphasis>AOR</emphasis> - user AOR in username[@domain]
				format (domain must be supplied only if use_domain option
				is on).
			</para></listitem>
			<listitem><para>
				<emphasis>contact</emphasis> - exact contact to be removed
			</para></listitem>
		</itemizedlist>
	</section>

	<section id="mi_ul_dump" xreflabel="ul_dump">
		<title>
		<function moreinfo="none">ul_dump</function>
		</title>
		<para>
		Dumps the entire content of the USRLOC in memory cache
		</para>
		<para>Parameters: </para>
		<itemizedlist>
			<listitem><para>
				<emphasis>brief</emphasis> - (optional, may not be present); if
				equals to string <quote>brief</quote>, a brief dump will be
				done (only AOR and contacts, with no other details)
			</para></listitem>
		</itemizedlist>
	</section>

	<section id="mi_ul_flush" xreflabel="ul_flush">
		<title>
		<function moreinfo="none">ul_flush</function>
		</title>
		<para>
		Force a flush of all pending usrloc cache changes to the database.
		Normally, this routine runs every
		<xref linkend="param_timer_interval"/> seconds.
		</para>
	</section>

	<section id="mi_ul_add" xreflabel="ul_add">
		<title>
		<function moreinfo="none">ul_add</function>
		</title>
		<para>
		Adds a new contact for an user AOR.
		</para>
		<para>Parameters: </para>
		<itemizedlist>
			<listitem><para>
				<emphasis>table name (string)</emphasis> - table where the contact
				will be added (Ex: "location").
			</para></listitem>
			<listitem><para>
				<emphasis>aor (string) </emphasis> - user AOR in username[@domain]
				format (domain must be supplied only if use_domain option
				is on).
			</para></listitem>
			<listitem><para>
				<emphasis>contact (string)</emphasis> - Contact URI to be added
			</para></listitem>
			<listitem><para>
				<emphasis>expires (int)</emphasis> - expires value of the contact
			</para></listitem>
			<listitem><para>
				<emphasis>q (string)</emphasis> - Q value of the contact
			</para></listitem>
			<listitem><para>
				<emphasis>flags (int)</emphasis> - internal USRLOC flags of the
				contact
			</para></listitem>
			<listitem><para>
				<emphasis>cflags (int)</emphasis> - per branch flags of the
				contact
			</para></listitem>
			<listitem><para>
				<emphasis>methods (int)</emphasis> - bitmask with supported requests
				of the contact.  To whitelist all SIP methods, simply use the
				value <emphasis role='bold'>32767</emphasis>. For a breakdown
				of each method's value, see the "request_method" internal enum.
			</para></listitem>
		</itemizedlist>
	</section>

	<section id="mi_ul_show_contact" xreflabel="ul_show_contact">
		<title>
		<function moreinfo="none">ul_show_contact</function>
		</title>
		<para>
		Dumps the contacts of an user AOR.
		</para>
		<para>Parameters: </para>
		<itemizedlist>
			<listitem><para>
				<emphasis>table_name</emphasis> - table where the AOR
				resides (Ex: location).
			</para></listitem>
			<listitem><para>
				<emphasis>aor</emphasis> - user AOR in username[@domain]
				format (domain must be supplied only if use_domain option
				is on).
			</para></listitem>
		</itemizedlist>
	</section>

	<section id="mi_ul_sync" xreflabel="ul_sync">
		<title>
		<function moreinfo="none">ul_sync</function>
		</title>
		<para>
		Empty the location table, then synchronize it with all contacts from
		memory.  Note that this can not be used when no database is specified
		or with the DB-Only scheme.
		</para>
		<para>
		Important: make sure that all your contacts are in memory
		(<emphasis>ul_dump</emphasis> MI function) before executing this
		command.
		</para>
		<para>Parameters: </para>
		<itemizedlist>
			<listitem><para>
				<emphasis>table name</emphasis> - table where the AOR
				resides (Ex: location).
			</para></listitem>
			<listitem><para>
				<emphasis>AOR (optional)</emphasis> - only delete/sync this
				user AOR, not the whole table.  Format: "username[@domain]"
				(<emphasis>domain</emphasis> is required only if
				<xref linkend="param_use_domain"/> option is on).
			</para></listitem>
		</itemizedlist>
	</section>

	<section id="mi_ul_cluster_sync" xreflabel="ul_cluster_sync">
		<title>
		<function moreinfo="none">ul_cluster_sync</function>
		</title>
		<para>
		This command will only take effect if the target OpenSIPS instance is
		paired with a hot backup instance, while running under a
		cluster-enabled <xref linkend="param_working_mode_preset"/>.
		</para>
		<para>
		The current node will locate a healthy donor node within the
		<xref linkend="param_location_cluster"/> and issue a sync request to
		it. The donor node will then proceed to push all of its user location
		data over to the current node, via the binary interface. The received
		data will be merged with existing data. Conflicting contacts (matched
		according to <xref linkend="param_matching_mode"/>) are overwritten
		only if the sync data is newer than the current data.
		</para>
	</section>

	</section>


	<section id="exported_statistics">
		<title>Exported Statistics</title>
		<para>
		Exported statistics are listed in the next sections.
		</para>
		<section id="stat_users" xreflabel="users">
		<title>users</title>
			<para>
			Number of AOR existing in the USRLOC memory cache for that domain
			- can not be resetted; this statistic will be register for each
			used domain (Ex: location).
			</para>
		</section>
		<section id="stat_contacts" xreflabel="contacts">
		<title>contacts</title>
			<para>
			Number of contacts existing in the USRLOC memory cache for that
			domain - can not be resetted; this statistic will be register for
			each used domain (Ex: location).
			</para>
		</section>
		<section id="stat_expires" xreflabel="expires">
		<title>expires</title>
			<para>
			Total number of expired contacts for that domain - can be resetted;
			 this statistic will be register for each used domain
			(Ex: location).
			</para>
		</section>
		<section id="stat_registered_users" xreflabel="registered_users">
		<title>registered_users</title>
			<para>
			Total number of AOR existing in the USRLOC memory cache for all
			domains - can not be resetted.
			</para>
		</section>
	</section>


	<section id="exported_events" xreflabel="Exported Events">
	<title>Exported Events</title>
	<section id="event_E_UL_AOR_INSERT" xreflabel="E_UL_AOR_INSERT">
		<title>
		<function moreinfo="none">E_UL_AOR_INSERT</function>
		</title>
		<para>
			This event is raised when a new AOR is inserted in the USRLOC
			memory cache.
		</para>
		<para>Parameters:</para>
		<itemizedlist>
			<listitem><para>
				<emphasis>domain</emphasis> - The name of the table.
			</para></listitem>
			<listitem><para>
				<emphasis>aor</emphasis> - The AOR of the inserted record.
			</para></listitem>
		</itemizedlist>
	</section>
	<section id="event_E_UL_AOR_DELETE" xreflabel="E_UL_AOR_DELETE">
		<title>
		<function moreinfo="none">E_UL_AOR_DELETE</function>
		</title>
		<para>
			This event is raised when a new AOR is deleted from the USRLOC
			memory cache.
		</para>
		<para>Parameters:</para>
		<itemizedlist>
			<listitem><para>
				<emphasis>domain</emphasis> - The name of the table.
			</para></listitem>
			<listitem><para>
				<emphasis>aor</emphasis> - The AOR of the deleted record.
			</para></listitem>
		</itemizedlist>
	</section>
	<section id="event_E_UL_CONTACT_INSERT" xreflabel="E_UL_CONTACT_INSERT">
		<title>
		<function moreinfo="none">E_UL_CONTACT_INSERT</function>
		</title>
		<para>
			This event is raised when a new contact is inserted in any of the
			existing AOR's contact list. For each new contact, if its AOR does
			not exist in the memory, then both the E_UL_AOR_CREATE and
			E_UL_CONTACT_INSERT events will be raised.
		</para>
		<para>Parameters:</para>
		<itemizedlist>
			<listitem><para>
				<emphasis>domain</emphasis> - The name of the table.
			</para></listitem>
			<listitem><para>
				<emphasis>aor</emphasis> - The AOR of the inserted contact.
			</para></listitem>
			<listitem><para>
				<emphasis>uri</emphasis> - The contact URI of the inserted
				contact.
			</para></listitem>
			<listitem><para>
				<emphasis>received</emphasis> - IP, port and protocol the
				registration message was received from. If these have the
				same value as the contact's address (see the address parameter)
				then the received parameter will be an empty string.
			</para></listitem>
			<listitem><para>
				<emphasis>path</emphasis> - The PATH header value of the
				registration message.(empty string if not present)
			</para></listitem>
			<listitem><para>
				<emphasis>qval</emphasis> - The Q value (priority) of the
				contact (as integer value from 0 to 10).
			</para></listitem>
			<listitem><para>
				<emphasis>user_agent</emphasis> - The User-Agent header
				value.
			</para>
			<para><emphasis>NOTICE: </emphasis>Can contain spaces.
			</para></listitem>
			<listitem><para>
				<emphasis>socket</emphasis> - The SIP socket/listener
				(as string) used by OpenSIPS to receive the contact
				registations.
			</para></listitem>
			<listitem><para>
				<emphasis>bflags</emphasis> - The branch flags (bflags) of the
				contact (in integer value of the bitmask)
			</para></listitem>
			<listitem><para>
				<emphasis>expires</emphasis> - The expires value of the
				contact (as UNIX timestamp integer).
			</para></listitem>
			<listitem><para>
				<emphasis>callid</emphasis> - The Call-ID header of the
				registration message.
			</para></listitem>
			<listitem><para>
				<emphasis>cseq</emphasis> - The cseq number as an int value.
			</para></listitem>
			<listitem><para>
				<emphasis>attr</emphasis> - The attributes string attached
				to the contact (the custom attributes attached from the
				script level). As this string is options, if missing in the
				contact, the event will push the empty string for this event
				field.
			</para></listitem>
			<listitem><para>
				<emphasis>latency</emphasis> - The latency of the last
				successful ping for this contact, in microseconds. Until the
				first ping reply for a given contact arrives, its pinging
				latency will be 0.
			</para></listitem>
			<listitem><para>
				<emphasis>shtag</emphasis> - The shared tag of the contact,
				which helps determine if the current node owns the contact
				(e.g. possibly using the <emphasis role='bold'>$cluster.sh_tag
				</emphasis> pseudo-variable in order to perform the check).
			</para>
			<para><emphasis>NOTICE: </emphasis>If a contact has no shared tag
				attached to it, the value of this parameter will be "" (empty
				string)!
			</para></listitem>
		</itemizedlist>
	</section>

	<section id="event_E_UL_CONTACT_DELETE" xreflabel="E_UL_CONTACT_DELETE">
		<title>
		<function moreinfo="none">E_UL_CONTACT_DELETE</function>
		</title>
		<para>
			This event is raised when a contact is deleted from an
			existing AOR's contact list. If the contact is the only one in
			the list then both the E_UL_AOR_DELETE and
			E_UL_CONTACT_DELETE events will be raised.
		</para>
		<para>Parameters: same as the
			<xref linkend="event_E_UL_CONTACT_INSERT"/> event</para>
	</section>

	<section id="event_E_UL_CONTACT_UPDATE" xreflabel="E_UL_CONTACT_UPDATE">
		<title>
		<function moreinfo="none">E_UL_CONTACT_UPDATE</function>
		</title>
		<para>
			This event is raised when a contact's info is updated by receiving
			another registration message.
		</para>
		<para>Parameters: same as the
			<xref linkend="event_E_UL_CONTACT_INSERT"/> event</para>
	</section>

	<section id="event_E_UL_CONTACT_REFRESH" xreflabel="E_UL_CONTACT_REFRESH">
		<title>
		<function moreinfo="none">E_UL_CONTACT_REFRESH</function>
		</title>
		<para>
			This event may only be raised for RFC 8599 (Push Notification)
			enabled contacts.
		</para>
		<para>
		Set <xref linkend="param_contact_refresh_timer"/> to
		<emphasis>true</emphasis> in order to enable this event.  The event is
		raised within reasonable time before an RFC 8599 enabled contact
		will expire, such that the script writer can take action,
		possibly force a registration refresh from the endpoint.
		</para>
		<para>Parameters:</para>
		<itemizedlist>
			<listitem><para>
				<emphasis>domain</emphasis> - The name of the table.
			</para></listitem>
			<listitem><para>
				<emphasis>aor</emphasis> - The AOR of the inserted contact.
			</para></listitem>
			<listitem><para>
				<emphasis>uri</emphasis> - The contact URI of the inserted
				contact.
			</para></listitem>
			<listitem><para>
				<emphasis>received</emphasis> - IP, port and protocol the
				registration message was received from. If these have the
				same value as the contact's address (see the address parameter)
				then the received parameter will be an empty string.
			</para></listitem>
			<listitem><para>
				<emphasis>user_agent</emphasis> - The User-Agent header
				value.
			</para>
			<para><emphasis>NOTICE: </emphasis>Can contain spaces.
			</para></listitem>
			<listitem><para>
				<emphasis>socket</emphasis> - The SIP socket/listener
				(as string) used by OpenSIPS to receive the contact
				registations.
			</para></listitem>
			<listitem><para>
				<emphasis>bflags</emphasis> - The branch flags (bflags) of the
				contact (in integer value of the bitmask)
			</para></listitem>
			<listitem><para>
				<emphasis>expires</emphasis> - The expires value of the
				contact (as UNIX timestamp integer).
			</para></listitem>
			<listitem><para>
				<emphasis>callid</emphasis> - The Call-ID header of the
				registration message.
			</para></listitem>
			<listitem><para>
				<emphasis>attr</emphasis> - The attributes string attached
				to the contact (the custom attributes attached from the
				script level). As this string is options, if missing in the
				contact, the event will push the empty string for this event
				field.
			</para></listitem>
			<listitem><para>
				<emphasis>shtag</emphasis> - The shared tag of the contact,
				which helps determine if the current node owns the contact
				(e.g. possibly using the <emphasis role='bold'>$cluster.sh_tag
				</emphasis> pseudo-variable in order to perform the check).
		</para></listitem>
			<listitem><para>
				<emphasis>reason</emphasis> - the reason why the binding refresh
				event was triggered.  Possible values:
				<itemizedlist>
					<listitem><para>
						"reg-refresh" - periodic refresh triggered by OpenSIPS
					</para></listitem>

					<listitem><para>
						"ini-INVITE", "ini-SUBSCRIBE", etc. - a refresh
						triggered by an incoming initial SIP request
					</para></listitem>

					<listitem><para>
						"mid-INVITE", "mid-BYE", etc. - a refresh triggered
						by an incoming mid-dialog SIP request
					</para></listitem>
				</itemizedlist>
			</para></listitem>
			<listitem><para>
				<emphasis>req_callid</emphasis> - the Call-ID of the SIP request
				which triggered this event, if any.  This gives the ability to
				logically link the pending request with the current event and
				access useful data from that request (e.g. caller identity,
				dialed number, etc.).
				</para>
				<para>
				Using the <emphasis>req_callid</emphasis>, if a dialog has been
				created for the pending request, this dialog may be temporarily
				loaded inside the event_route using the
				<ulink url='dialog#func_load_dialog_ctx'>load_dialog_ctx()</ulink> and
				<ulink url='dialog#func_unload_dialog_ctx'>unload_dialog_ctx()</ulink>
				functions of the dialog module.
				</para>
			</listitem>
		</itemizedlist>
	</section>

	<section id="event_E_UL_LATENCY_UPDATE" xreflabel="E_UL_LATENCY_UPDATE">
		<title>
		<function moreinfo="none">E_UL_LATENCY_UPDATE</function>
		</title>
		<para>
		This event is raised when a contact pinging latency matches either
		of the <xref linkend="param_latency_event_min_us"/> or
		<xref linkend="param_latency_event_min_us_delta"/> filters. If none of
		these filters is set, this event will get raised for each successful
		contact ping operation.
		</para>
		<para>Parameters: same as the
			<xref linkend="event_E_UL_CONTACT_INSERT"/> event</para>
	</section>

	</section>
</chapter>
