Tune DS replication settings

Tune 389-DS replication settings to improve performance and avoid
timeouts. During installation of a replica, the value of
nsDS5ReplicaBindDnGroupCheckInterval is reduced to 2 seconds. At the end
of the installation, the value is increased sensible production
settings. This avoids long delays during replication.

See: https://pagure.io/freeipa/issue/7617
Signed-off-by: Christian Heimes <cheimes@redhat.com>
Reviewed-By: Tibor Dudlak <tdudlak@redhat.com>
This commit is contained in:
Christian Heimes
2018-07-09 18:20:17 +02:00
parent 199d50a4c8
commit 811b0fdb46
6 changed files with 124 additions and 44 deletions
+7 -1
View File
@@ -419,6 +419,9 @@ class CAInstance(DogtagInstance):
self.teardown_admin) self.teardown_admin)
self.step("starting certificate server instance", self.step("starting certificate server instance",
self.start_instance) self.start_instance)
if promote:
self.step("Finalize replication settings",
self.finalize_replica_config)
# Step 1 of external is getting a CSR so we don't need to do these # Step 1 of external is getting a CSR so we don't need to do these
# steps until we get a cert back from the external CA. # steps until we get a cert back from the external CA.
if self.external != 1: if self.external != 1:
@@ -1253,13 +1256,16 @@ class CAInstance(DogtagInstance):
api.Backend.ldap2.add_entry(entry) api.Backend.ldap2.add_entry(entry)
def __setup_replication(self): def __setup_replication(self):
repl = replication.CAReplicationManager(self.realm, self.fqdn) repl = replication.CAReplicationManager(self.realm, self.fqdn)
repl.setup_cs_replication(self.master_host) repl.setup_cs_replication(self.master_host)
# Activate Topology for o=ipaca segments # Activate Topology for o=ipaca segments
self.__update_topology() self.__update_topology()
def finalize_replica_config(self):
repl = replication.CAReplicationManager(self.realm, self.fqdn)
repl.finalize_replica_config(self.master_host)
def __enable_instance(self): def __enable_instance(self):
basedn = ipautil.realm_to_suffix(self.realm) basedn = ipautil.realm_to_suffix(self.realm)
if not self.clone: if not self.clone:
+30 -18
View File
@@ -418,6 +418,20 @@ class DsInstance(service.Service):
self.start_creation(runtime=30) self.start_creation(runtime=30)
def _get_replication_manager(self):
# Always connect to self over ldapi
ldap_uri = ipaldap.get_ldap_uri(protocol='ldapi', realm=self.realm)
conn = ipaldap.LDAPClient(ldap_uri)
conn.external_bind()
repl = replication.ReplicationManager(
self.realm, self.fqdn, self.dm_password, conn=conn
)
if self.dm_password is not None and not self.promote:
bind_dn = DN(('cn', 'Directory Manager'))
bind_pw = self.dm_password
else:
bind_dn = bind_pw = None
return repl, bind_dn, bind_pw
def __setup_replica(self): def __setup_replica(self):
""" """
@@ -434,26 +448,24 @@ class DsInstance(service.Service):
self.realm, self.realm,
self.dm_password) self.dm_password)
# Always connect to self over ldapi repl, bind_dn, bind_pw = self._get_replication_manager()
ldap_uri = ipaldap.get_ldap_uri(protocol='ldapi', realm=self.realm) repl.setup_promote_replication(
conn = ipaldap.LDAPClient(ldap_uri) self.master_fqdn,
conn.external_bind() r_binddn=bind_dn,
repl = replication.ReplicationManager(self.realm, r_bindpw=bind_pw,
self.fqdn, cacert=self.ca_file
self.dm_password, conn=conn) )
if self.dm_password is not None and not self.promote:
bind_dn = DN(('cn', 'Directory Manager'))
bind_pw = self.dm_password
else:
bind_dn = bind_pw = None
repl.setup_promote_replication(self.master_fqdn,
r_binddn=bind_dn,
r_bindpw=bind_pw,
cacert=self.ca_file)
self.run_init_memberof = repl.needs_memberof_fixup() self.run_init_memberof = repl.needs_memberof_fixup()
def finalize_replica_config(self):
repl, bind_dn, bind_pw = self._get_replication_manager()
repl.finalize_replica_config(
self.master_fqdn,
r_binddn=bind_dn,
r_bindpw=bind_pw,
cacert=self.ca_file
)
def __configure_sasl_mappings(self): def __configure_sasl_mappings(self):
# we need to remove any existing SASL mappings in the directory as otherwise they # we need to remove any existing SASL mappings in the directory as otherwise they
# they may conflict. # they may conflict.
+72 -20
View File
@@ -75,6 +75,20 @@ STRIP_ATTRS = ('modifiersName',
'internalModifiersName', 'internalModifiersName',
'internalModifyTimestamp') 'internalModifyTimestamp')
# settings for cn=replica,cn=$DB,cn=mapping tree,cn=config
# during replica installation
REPLICA_CREATION_SETTINGS = {
"nsds5ReplicaReleaseTimeout": ["20"],
"nsds5ReplicaBackoffMax": ["3"],
"nsDS5ReplicaBindDnGroupCheckInterval": ["2"]
}
# after replica installation
REPLICA_FINAL_SETTINGS = {
"nsds5ReplicaReleaseTimeout": ["60"],
"nsds5ReplicaBackoffMax": ["300"], # default
"nsDS5ReplicaBindDnGroupCheckInterval": ["60"]
}
def replica_conn_check(master_host, host_name, realm, check_ca, def replica_conn_check(master_host, host_name, realm, check_ca,
dogtag_master_ds_port, admin_password=None, dogtag_master_ds_port, admin_password=None,
@@ -201,9 +215,13 @@ def wait_for_entry(connection, dn, timeout, attr=None, attrvalue='*',
class ReplicationManager(object): class ReplicationManager(object):
"""Manage replication agreements between DS servers, and sync """Manage replication agreements
agreements with Windows servers"""
def __init__(self, realm, hostname, dirman_passwd, port=PORT, starttls=False, conn=None): between DS servers, and sync agreements with Windows servers
"""
def __init__(self, realm, hostname, dirman_passwd=None, port=PORT,
starttls=False, conn=None):
self.hostname = hostname self.hostname = hostname
self.port = port self.port = port
self.dirman_passwd = dirman_passwd self.dirman_passwd = dirman_passwd
@@ -481,22 +499,16 @@ class ReplicationManager(object):
except errors.NotFound: except errors.NotFound:
pass pass
else: else:
managers = {DN(m) for m in entry.get('nsDS5ReplicaBindDN', [])} binddns = entry.setdefault('nsDS5ReplicaBindDN', [])
if replica_binddn not in {DN(m) for m in binddns}:
mods = []
if replica_binddn not in managers:
# Add the new replication manager # Add the new replication manager
mods.append( binddns.append(replica_binddn)
(ldap.MOD_ADD, 'nsDS5ReplicaBindDN', replica_binddn) for key, value in REPLICA_CREATION_SETTINGS.items():
) entry[key] = value
if 'nsds5replicareleasetimeout' not in entry: try:
# See https://pagure.io/freeipa/issue/7488 conn.update_entry(entry)
mods.append( except errors.EmptyModlist:
(ldap.MOD_ADD, 'nsds5replicareleasetimeout', ['60']) pass
)
if mods:
conn.modify_s(dn, mods)
self.set_replica_binddngroup(conn, entry) self.set_replica_binddngroup(conn, entry)
@@ -515,9 +527,8 @@ class ReplicationManager(object):
nsds5flags=["1"], nsds5flags=["1"],
nsds5replicabinddn=[replica_binddn], nsds5replicabinddn=[replica_binddn],
nsds5replicabinddngroup=[self.repl_man_group_dn], nsds5replicabinddngroup=[self.repl_man_group_dn],
nsds5replicabinddngroupcheckinterval=["60"],
nsds5replicareleasetimeout=["60"],
nsds5replicalegacyconsumer=["off"], nsds5replicalegacyconsumer=["off"],
**REPLICA_CREATION_SETTINGS
) )
conn.add_entry(entry) conn.add_entry(entry)
@@ -543,6 +554,47 @@ class ReplicationManager(object):
except errors.DuplicateEntry: except errors.DuplicateEntry:
return return
def _finalize_replica_settings(self, conn):
"""Change replica settings to final values
During replica installation, some settings are configured for faster
replication.
"""
dn = self.replica_dn()
entry = conn.get_entry(dn)
for key, value in REPLICA_FINAL_SETTINGS.items():
entry[key] = value
try:
conn.update_entry(entry)
except errors.EmptyModlist:
pass
def finalize_replica_config(self, r_hostname, r_binddn=None,
r_bindpw=None, cacert=paths.IPA_CA_CRT):
"""Apply final cn=replica settings
replica_config() sets several attribute to fast cache invalidation
and fast reconnects to optimize replicat installation. For
production, longer timeouts and less aggressive cache invalidation
is sufficient. finalize_replica_config() sets the values on new
replica and the master.
When installing multiple replicas in parallel, one replica may
finalize the values while another is still installing.
See https://pagure.io/freeipa/issue/7617
"""
self._finalize_replica_settings(self.conn)
ldap_uri = ipaldap.get_ldap_uri(r_hostname)
r_conn = ipaldap.LDAPClient(ldap_uri, cacert=cacert)
if r_bindpw:
r_conn.simple_bind(r_binddn, r_bindpw)
else:
r_conn.gssapi_bind()
self._finalize_replica_settings(r_conn)
r_conn.close()
def setup_chaining_backend(self, conn): def setup_chaining_backend(self, conn):
chaindn = DN(('cn', 'chaining database'), ('cn', 'plugins'), ('cn', 'config')) chaindn = DN(('cn', 'chaining database'), ('cn', 'plugins'), ('cn', 'config'))
benamebase = "chaindb" benamebase = "chaindb"
@@ -1505,6 +1505,8 @@ def install(installer):
# Apply any LDAP updates. Needs to be done after the replica is synced-up # Apply any LDAP updates. Needs to be done after the replica is synced-up
service.print_msg("Applying LDAP updates") service.print_msg("Applying LDAP updates")
ds.apply_updates() ds.apply_updates()
service.print_msg("Finalize replication settings")
ds.finalize_replica_config()
if kra_enabled: if kra_enabled:
kra.install(api, config, options, custodia=custodia) kra.install(api, config, options, custodia=custodia)
+8 -4
View File
@@ -48,6 +48,7 @@ from ipaserver.install import dnskeysyncinstance
from ipaserver.install import dogtaginstance from ipaserver.install import dogtaginstance
from ipaserver.install import krbinstance from ipaserver.install import krbinstance
from ipaserver.install import adtrustinstance from ipaserver.install import adtrustinstance
from ipaserver.install import replication
from ipaserver.install.upgradeinstance import IPAUpgrade from ipaserver.install.upgradeinstance import IPAUpgrade
from ipaserver.install.ldapupdate import BadSyntax from ipaserver.install.ldapupdate import BadSyntax
@@ -1680,11 +1681,14 @@ def update_replica_config(db_suffix):
except ipalib.errors.NotFound: except ipalib.errors.NotFound:
return # entry does not exist until a replica is installed return # entry does not exist until a replica is installed
if 'nsds5replicareleasetimeout' not in entry: for key, value in replication.REPLICA_FINAL_SETTINGS.items():
# See https://pagure.io/freeipa/issue/7488 entry[key] = value
logger.info("Adding nsds5replicaReleaseTimeout=60 to %s", dn) try:
entry['nsds5replicareleasetimeout'] = '60'
api.Backend.ldap2.update_entry(entry) api.Backend.ldap2.update_entry(entry)
except ipalib.errors.EmptyModlist:
pass
else:
logger.info("Updated entry %s", dn)
def migrate_to_authselect(): def migrate_to_authselect():
@@ -148,7 +148,11 @@ class TestExternalCA(IntegrationTest):
'-LLL', '-LLL',
'-o', '-o',
'ldif-wrap=no']) 'ldif-wrap=no'])
assert 'nsds5ReplicaReleaseTimeout: 60' in result.stdout_text # case insensitive match
text = result.stdout_text.lower()
# see ipaserver.install.replication.REPLICA_FINAL_SETTINGS
assert 'nsds5ReplicaReleaseTimeout: 60'.lower() in text
assert 'nsDS5ReplicaBindDnGroupCheckInterval: 60'.lower() in text
def test_client_installation_with_otp(self): def test_client_installation_with_otp(self):
# Test for issue 7526: client installation fails with one-time # Test for issue 7526: client installation fails with one-time