From 5c2cad20d7e80cfd55db710ce637f7034e115a52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Mart=C3=ADnez?= Date: Wed, 31 Aug 2016 15:30:31 +0200 Subject: [PATCH 1/7] Add custom_conf configuration parameter This will be used in place of postgresql.base.conf, to be included on the main postgresql.conf. --- patroni/postgresql.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/patroni/postgresql.py b/patroni/postgresql.py index 37d09460..a189acc4 100644 --- a/patroni/postgresql.py +++ b/patroni/postgresql.py @@ -100,9 +100,13 @@ class Postgresql(object): self._postgresql_conf = os.path.join(self._data_dir, config_base_name + '.conf') self._postgresql_base_conf_name = config_base_name + '.base.conf' self._postgresql_base_conf = os.path.join(self._data_dir, self._postgresql_base_conf_name) + self._postgresql_custom_conf = config.get('custom_conf') self._recovery_conf = os.path.join(self._data_dir, 'recovery.conf') - self._configuration_to_save = (self._postgresql_conf, self._postgresql_base_conf, - os.path.join(self._data_dir, 'pg_hba.conf')) + self._configuration_to_save = [self._postgresql_conf] + if not self._postgresql_custom_conf: + self._configuration_to_save.append(self._postgresql_base_conf) + if not config['parameters'].get('hba_file'): + self._configuration_to_save.append(os.path.join(self._data_dir, 'pg_hba.conf')) self._postmaster_pid = os.path.join(self._data_dir, 'postmaster.pid') self._trigger_file = config.get('recovery_conf', {}).get('trigger_file') or 'promote' self._trigger_file = os.path.abspath(os.path.join(self._data_dir, self._trigger_file)) @@ -622,12 +626,12 @@ class Postgresql(object): def _write_postgresql_conf(self): # rename the original configuration if it is necessary - if not os.path.exists(self._postgresql_base_conf): + if not self._postgresql_custom_conf and not os.path.exists(self._postgresql_base_conf): os.rename(self._postgresql_conf, self._postgresql_base_conf) with open(self._postgresql_conf, 'w') as f: f.write('# Do not edit this file manually!\n# It will be overwritten by Patroni!\n') - f.write("include '{0}'\n\n".format(self._postgresql_base_conf_name)) + f.write("include '{0}'\n\n".format(self._postgresql_custom_conf or self._postgresql_base_conf_name)) for name, value in sorted(self._server_parameters.items()): if name not in self.CMDLINE_OPTIONS: f.write("{0} = '{1}'\n".format(name, value)) From 1fb562e118cc92c5749393bddd89f2acbe430d98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Mart=C3=ADnez?= Date: Wed, 31 Aug 2016 15:38:42 +0200 Subject: [PATCH 2/7] Add custom_conf parameter documentation --- docs/SETTINGS.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/SETTINGS.rst b/docs/SETTINGS.rst index 24b01dd3..6d5894ea 100644 --- a/docs/SETTINGS.rst +++ b/docs/SETTINGS.rst @@ -69,6 +69,7 @@ PostgreSQL - **listen**: IP address + port that Postgres listens to; must be accessible from other nodes in the cluster, if you're using streaming replication. Multiple comma-separated addresses are permitted, as long as the port component is appended after to the last one with a colon, i.e. ``listen: 127.0.0.1,127.0.0.2:5432``. Patroni will use the first address from this list to establish local connections to the PostgreSQL node. - **pgpass**: path to the `.pgpass `__ password file. Patroni creates this file before executing pg\_basebackup and under some other circumstances. The location must be writable by Patroni. - **recovery\_conf**: additional configuration settings written to recovery.conf when configuring follower. +- **custom_conf** : path to a custom `postgresql.conf` file, that will be used in place of `postgresql.base.conf`. The file must exist and will be included from its location on the real `postgresql.conf`. - **parameters**: list of configuration settings for Postgres. Many of these are required for replication to work. - **pg\_ctl\_timeout**: How long should pg_ctl wait when doing ``start``, ``stop`` or ``restart``. Default value is 60 seconds. - **use\_pg\_rewind**: try to use pg\_rewind on the former leader when it joins cluster as a replica. From a642860ae8d0798f8d4145b2778bf9d988567f42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Mart=C3=ADnez?= Date: Thu, 1 Sep 2016 17:06:33 +0200 Subject: [PATCH 3/7] Turn _configuration_to_save into a property method --- patroni/postgresql.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/patroni/postgresql.py b/patroni/postgresql.py index a189acc4..0baff425 100644 --- a/patroni/postgresql.py +++ b/patroni/postgresql.py @@ -102,11 +102,6 @@ class Postgresql(object): self._postgresql_base_conf = os.path.join(self._data_dir, self._postgresql_base_conf_name) self._postgresql_custom_conf = config.get('custom_conf') self._recovery_conf = os.path.join(self._data_dir, 'recovery.conf') - self._configuration_to_save = [self._postgresql_conf] - if not self._postgresql_custom_conf: - self._configuration_to_save.append(self._postgresql_base_conf) - if not config['parameters'].get('hba_file'): - self._configuration_to_save.append(os.path.join(self._data_dir, 'pg_hba.conf')) self._postmaster_pid = os.path.join(self._data_dir, 'postmaster.pid') self._trigger_file = config.get('recovery_conf', {}).get('trigger_file') or 'promote' self._trigger_file = os.path.abspath(os.path.join(self._data_dir, self._trigger_file)) @@ -128,6 +123,15 @@ class Postgresql(object): self.set_role('master' if self.is_leader() else 'replica') self._write_postgresql_conf() # we are "joining" already running postgres + @property + def _configuration_to_save(self): + configuration = [self._postgresql_conf] + if not self._postgresql_custom_conf: + configuration.append(self._postgresql_base_conf) + if not self.config['parameters'].get('hba_file'): + configuration.append(os.path.join(self._data_dir, 'pg_hba.conf')) + return configuration + @property def use_slots(self): return self._use_slots and self._major_version >= 9.4 From f58ff3a96f7f93bfaba2b023ecdbbb76c3e01007 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Mart=C3=ADnez?= Date: Thu, 1 Sep 2016 17:59:47 +0200 Subject: [PATCH 4/7] Document custom_conf parameter --- docs/SETTINGS.rst | 2 +- docs/dynamic_configuration.rst | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/docs/SETTINGS.rst b/docs/SETTINGS.rst index 6d5894ea..aa6b3e31 100644 --- a/docs/SETTINGS.rst +++ b/docs/SETTINGS.rst @@ -69,7 +69,7 @@ PostgreSQL - **listen**: IP address + port that Postgres listens to; must be accessible from other nodes in the cluster, if you're using streaming replication. Multiple comma-separated addresses are permitted, as long as the port component is appended after to the last one with a colon, i.e. ``listen: 127.0.0.1,127.0.0.2:5432``. Patroni will use the first address from this list to establish local connections to the PostgreSQL node. - **pgpass**: path to the `.pgpass `__ password file. Patroni creates this file before executing pg\_basebackup and under some other circumstances. The location must be writable by Patroni. - **recovery\_conf**: additional configuration settings written to recovery.conf when configuring follower. -- **custom_conf** : path to a custom `postgresql.conf` file, that will be used in place of `postgresql.base.conf`. The file must exist and will be included from its location on the real `postgresql.conf`. +- **custom_conf** : path to an optional custom ``postgresql.conf`` file, that will be used in place of ``postgresql.base.conf``. The file must exist on all cluster nodes, be readable by PostgreSQL and will be included from its location on the real ``postgresql.conf``. Note that Patroni will not monitor this file for changes, nor backup it. However, its settings can still be overriden by Patroni's own configuration facilities - see `dynamic configuration `__ for details. - **parameters**: list of configuration settings for Postgres. Many of these are required for replication to work. - **pg\_ctl\_timeout**: How long should pg_ctl wait when doing ``start``, ``stop`` or ``restart``. Default value is 60 seconds. - **use\_pg\_rewind**: try to use pg\_rewind on the former leader when it joins cluster as a replica. diff --git a/docs/dynamic_configuration.rst b/docs/dynamic_configuration.rst index 9aa100dd..ce773b60 100644 --- a/docs/dynamic_configuration.rst +++ b/docs/dynamic_configuration.rst @@ -48,23 +48,24 @@ To be on the safe side parameters from the above lists are not written into ``po When applying the local or dynamic configuration options, the following actions are taken: -- The node first checks if there is a postgresql.base.conf. -- If it exists, it contains the renamed "original" configuration. -- If it doesn't, the original postgresql.conf is taken and renamed to postgresql.base.conf. +- The node first checks if there is a postgresql.base.conf or if the ``custom_conf`` parameter is set. +- If the `custom_conf` parameter is set, it will take the file specified on it as a base configuration, ignoring `postgresql.base.conf` and `postgresql.conf`. +- If the `custom_conf` parameter is not set and `postgresql.base.conf` exists, it contains the renamed "original" configuration and it will be used as a base configuration. +- If there is no `custom_conf` nor `postgresql.base.conf`, the original postgresql.conf is taken and renamed to postgresql.base.conf. - The dynamic options (with the exceptions above) are dumped into the postgresql.conf and an include is set in - postgresql.conf to postgresql.base.conf. Therefore, we would be able to apply new options without re-reading the configuration file to check if the include is present not. + postgresql.conf to the used base configuration (either postgresql.base.conf or what is on ``custom_conf``). Therefore, we would be able to apply new options without re-reading the configuration file to check if the include is present not. - Some parameters that are essential for Patroni to manage the cluster are overridden using the command line. - If some of the options that require restart are changed (we should look at the context in pg_settings and at the actual values of those options), a pending_restart flag of a given node is set. This flag is reset on any restart. The parameters would be applied in the following order (run-time are given the highest priority): -1. load parameters from file `postgresql.base.conf` +1. load parameters from file `postgresql.base.conf` (or from a `custom_conf` file, if set) 2. load parameters from file `postgresql.conf` 3. load parameters from file `postgresql.auto.conf` 4. run-time parameter using `-o --name=value` -This allows configuration for all the nodes (2), configuration for a specific node using `ALTER SYSTEM` (3) and ensures that parameters essential to the running of Patroni are enforced. (4) +This allows configuration for all the nodes (2), configuration for a specific node using `ALTER SYSTEM` (3) and ensures that parameters essential to the running of Patroni are enforced (4), as well as leaves room for configuration tools that manage `postgresql.conf` directly without involving Patroni (1). Also, the following Patroni configuration options can be changed only dynamically: From 07e95912a231e69b5403a35e974a02c924606ac6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Mart=C3=ADnez?= Date: Fri, 2 Sep 2016 16:22:11 +0200 Subject: [PATCH 5/7] Fetch custom_conf dynamically --- patroni/postgresql.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/patroni/postgresql.py b/patroni/postgresql.py index 0baff425..62c7809d 100644 --- a/patroni/postgresql.py +++ b/patroni/postgresql.py @@ -100,7 +100,6 @@ class Postgresql(object): self._postgresql_conf = os.path.join(self._data_dir, config_base_name + '.conf') self._postgresql_base_conf_name = config_base_name + '.base.conf' self._postgresql_base_conf = os.path.join(self._data_dir, self._postgresql_base_conf_name) - self._postgresql_custom_conf = config.get('custom_conf') self._recovery_conf = os.path.join(self._data_dir, 'recovery.conf') self._postmaster_pid = os.path.join(self._data_dir, 'postmaster.pid') self._trigger_file = config.get('recovery_conf', {}).get('trigger_file') or 'promote' @@ -126,7 +125,7 @@ class Postgresql(object): @property def _configuration_to_save(self): configuration = [self._postgresql_conf] - if not self._postgresql_custom_conf: + if not config.get('custom_conf'): configuration.append(self._postgresql_base_conf) if not self.config['parameters'].get('hba_file'): configuration.append(os.path.join(self._data_dir, 'pg_hba.conf')) @@ -630,12 +629,12 @@ class Postgresql(object): def _write_postgresql_conf(self): # rename the original configuration if it is necessary - if not self._postgresql_custom_conf and not os.path.exists(self._postgresql_base_conf): + if not config.get('custom_conf') and not os.path.exists(self._postgresql_base_conf): os.rename(self._postgresql_conf, self._postgresql_base_conf) with open(self._postgresql_conf, 'w') as f: f.write('# Do not edit this file manually!\n# It will be overwritten by Patroni!\n') - f.write("include '{0}'\n\n".format(self._postgresql_custom_conf or self._postgresql_base_conf_name)) + f.write("include '{0}'\n\n".format(config.get('custom_conf') or self._postgresql_base_conf_name)) for name, value in sorted(self._server_parameters.items()): if name not in self.CMDLINE_OPTIONS: f.write("{0} = '{1}'\n".format(name, value)) From 80abe67ed2b4572c28363f6ef8ef3033ae9befe9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Mart=C3=ADnez?= Date: Fri, 2 Sep 2016 16:42:40 +0200 Subject: [PATCH 6/7] Fix missing self --- patroni/postgresql.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/patroni/postgresql.py b/patroni/postgresql.py index 62c7809d..879adb94 100644 --- a/patroni/postgresql.py +++ b/patroni/postgresql.py @@ -125,7 +125,7 @@ class Postgresql(object): @property def _configuration_to_save(self): configuration = [self._postgresql_conf] - if not config.get('custom_conf'): + if not self.config.get('custom_conf'): configuration.append(self._postgresql_base_conf) if not self.config['parameters'].get('hba_file'): configuration.append(os.path.join(self._data_dir, 'pg_hba.conf')) @@ -629,12 +629,12 @@ class Postgresql(object): def _write_postgresql_conf(self): # rename the original configuration if it is necessary - if not config.get('custom_conf') and not os.path.exists(self._postgresql_base_conf): + if not self.config.get('custom_conf') and not os.path.exists(self._postgresql_base_conf): os.rename(self._postgresql_conf, self._postgresql_base_conf) with open(self._postgresql_conf, 'w') as f: f.write('# Do not edit this file manually!\n# It will be overwritten by Patroni!\n') - f.write("include '{0}'\n\n".format(config.get('custom_conf') or self._postgresql_base_conf_name)) + f.write("include '{0}'\n\n".format(self.config.get('custom_conf') or self._postgresql_base_conf_name)) for name, value in sorted(self._server_parameters.items()): if name not in self.CMDLINE_OPTIONS: f.write("{0} = '{1}'\n".format(name, value)) From d98f255b64053b59e038e2990f6758129f95a1f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Mart=C3=ADnez?= Date: Fri, 2 Sep 2016 16:44:40 +0200 Subject: [PATCH 7/7] Do not fetch values in configuration checks --- patroni/postgresql.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/patroni/postgresql.py b/patroni/postgresql.py index 879adb94..1c06f2fc 100644 --- a/patroni/postgresql.py +++ b/patroni/postgresql.py @@ -125,7 +125,7 @@ class Postgresql(object): @property def _configuration_to_save(self): configuration = [self._postgresql_conf] - if not self.config.get('custom_conf'): + if 'custom_conf' not in self.config: configuration.append(self._postgresql_base_conf) if not self.config['parameters'].get('hba_file'): configuration.append(os.path.join(self._data_dir, 'pg_hba.conf')) @@ -629,7 +629,7 @@ class Postgresql(object): def _write_postgresql_conf(self): # rename the original configuration if it is necessary - if not self.config.get('custom_conf') and not os.path.exists(self._postgresql_base_conf): + if 'custom_conf' not in self.config and not os.path.exists(self._postgresql_base_conf): os.rename(self._postgresql_conf, self._postgresql_base_conf) with open(self._postgresql_conf, 'w') as f: