The purpose of this RFE is that replication agreements logs debug messages with a session tracking identifier and provide to the consumer the same identifier along with each request it is sending. The consumer will log the provided identifier in the access log (see server support of session identifier).
Replication is a complexe component that makes diagnostic difficult. The goal of the RFE is to reduce complexity of the diagnostic of replication agreements that are responsible to run the supplier side of the replication protocol.
A replication agreement replicates updates from a supplier LDAP instance to a consumer LDAP instance. A replication topology usually contains several suppliers and multiple replicated backends. So the access logs of a consumer typically contains multiple requests all mixed together from multiple suppliers and for multiple backends. This is exactly the same mix of debug logs on the supplier error logs. It is time consuming and difficult (sometime even not possible) to correlate requests in consumer access logs with replication agreements debug logs in the supplier error logs.
To retrieve the operations associated with a given identifier, one can
The session tracking identifier must be unique. It is sent using control LDAP_CONTROL_X_SESSION_TRACKING (see Internet draft). The goal is that the admin, using ‘/usr/bin/grep
The Internet draft defines the control like
SessionIdentifierControlValue ::= SEQUENCE {
sessionSourceIp LDAPString,
sessionSourceName LDAPString,
formatOID LDAPOID,
sessionTrackingIdentifier LDAPString
}
389-DS implementation logs only sessionTrackingIdentifier. So the client should only set this field in the control.
On the client side (supplier) the session identifier is logged in error logs, when the replication debug level is enabled
[17/Apr/2025:16:16:12.717536265 +0200] - DEBUG - NSMMReplicationPlugin - repl5_inc_run - "EWBpte8J8Wx 2" - agmt="cn=004" (localhost:39004): State: wait_for_changes -> ready_to_acquire_replica
The session identifier is a string of 15 chars. The string contains two fields separated by a blank (‘ ‘). The first field is 11 characters. It contains the header of a base 64 encoded value. The second field is a counter [1,999] that identify the number of the opened replication connection. In the above example it is
EWBpte8J8Wx 2
The string is 15 chars long.
The First field identifies, in the replicated topology, the supplier host and backend. The initial value is a string “*
The Second field identifier the number of the replication connection from the supplier to the consumer. It ranges from [1,999]. It is reset to 1 when the supplier opens the 1000th connection.
The server side is already implemented so this chapters only details the client side
During replication agreement initialization (agmt_new_from_entry) (see repl5agmt)
The supplier code is impacted in two ways
The supplier sends the control to the consumer within each LDAP operations sent by the replication agreement. Hopefully all these operations are sent by ‘perform_operation’ and it is the perfect place to add the session tracking control. This routine uses the ‘conn’ parameter that refer to the agreement ‘conn->agmt’ where the session tracking value is stored (see repl5agmt). This routine creates a control (calling create_sessiontracking_ctrl) and adds it to the array of controls. Once the operation callback is called (e.g. ldap_modify_ext) it frees the control via ldap_control_free.
The supplier logs the session identifier in those set of replication debugging messages
During replication agreement deletion (agmt_delete) session_id_prefix is freed (see repl5agmt)
Control.c contains create_sessiontracking_ctrl that creates a session tracking control with sessionTrackingIdentifier set with a provided string.
Because the LDAP client can fill the access log with extra strings, the use of this control is restricted to authenticated users. This is enforced with this acl
dn: oid=1.3.6.1.4.1.21008.108.63.1,cn=features,cn=config
objectClass: top
objectClass: directoryServerFeature
oid: 1.3.6.1.4.1.21008.108.63.1
cn: Session Tracking Control
aci: (targetattr != "aci")(version 3.0; acl "Session Tracking Control";
allow (read,search) userdn = "ldap:///all";)
To restrict this use, the userdn may be replaced with a groupdn of all the replication manager accounts.
The session tracking value is stored in the replication agreement itself with 4 new fields
typedef struct repl5agmt
{
...
#define SID_SUPPORT_UNKNOWN 0
#define SID_SUPPORT_NO 1
#define SID_SUPPORT_YES 2
int session_tracking_supported; /* does the consumer support the control */
int session_id_cnt; /* use to differentiate connections */
char *session_id_prefix; /* use for debugging purpose on server/client sides. Computed once */
#define SESSION_ID_STR_SZ 15
char session_id[SESSION_ID_STR_SZ + 49];
...
} repl5agmt;
session_tracking_supported is a flag initialized with SID_SUPPORT_UNKNOWN. When the first replication session occurs it checks if the consumer supports or not the control and set the flag appropriately.
session_id_cnt is a counter that range [1,999]. It is incremented up to 999 then reset to 1. It is incremented each time the replication agreement opens a connection to the consumer (conn_connect_with_bootstrap).
session_id_prefix is a hash that represent a given replication agreement in the all topology. It is computed once when the replication agreement structure is created agmt_new_from_entry. It contains the 11 first chars of a base64 encoded value. The value is the result of a SHA-1 hash of the string “*
session_id is the actual sessionTrackingIdentifier logged in the supplier’s error logs and the sent to the consumer (using the *SessiontTrackingControl) to be logged in the consumer’s access logs. It identifies a connection between the supplier’s replication agreement and the consumer.
In python it can be tested with
from ldap.controls.sessiontrack import SessionTrackingControl, SESSION_TRACKING_CONTROL_OID
from ldap.extop import ExtendedRequest
st_ctrl = SessionTrackingControl(
'10.1.2.3'
'localhost.localdomain',
SESSION_TRACKING_CONTROL_OID + '.1.2.3.4',
'debugID 123456'
)
extop = ExtendedRequest(requestName = SESSION_TRACKING_CONTROL_OID, requestValue=None)
(oid_response, res) = topology_st.standalone.extop_s(extop, serverctrls=[st_ctrl])
[17/Oct/2024:17:21:51.930944766 +0200] conn=2 op=7 SRCH base="dc=example,dc=com" scope=2 filter="(objectClass=*)" attrs="distinguishedName"
[17/Oct/2024:17:21:51.931113128 +0200] conn=2 op=7 RESULT err=0 tag=101 nentries=1 wtime=0.000189515 optime=0.000171470 etime=0.000358345 notes=U,P details="Partially Unindexed Filter,Paged Search" pr_idx=0 pr_cookie=-1 sid="debugID 123456"
The server side of session tracking identifier is available starting in 2.6 and 3.0.
The replication client side is available starting 2.7 and 3.1.
So using 2.7+ (or 3.1+) replication topology, it is suffisant to enable debug replication logs (nsslapd-errorlog-level=8192) to get session identifier in both errors/access logs.
To enable the session tracking log, the replication debug level must be enabled (nsslapd-errorlog-level=8192).
Server side of session tracking
Client side of session tracking