Skip to content

Commit 80e9573

Browse files
authored
feat: pg_autoctl node — declarative node lifecycle from a single ini file (#1131)
* feat: pg_autoctl node — declarative node lifecycle from pg_autoctl_node.ini Adds a new pg_autoctl node sub-command tree and a NodeSpec file format designed as the recommended entry-point for container and Kubernetes deployments. ## pg_autoctl_node.ini sections [node] kind (postgres|monitor|coordinator|worker), name, hostname, port [postgresql] pgdata [monitor] pguri (empty for monitor nodes), no_monitor, node_id [formation] name, group [settings] candidate_priority, replication_quorum ← mutable, applied live [options] ssl, auth, pg_hba_lan ← create-time only [ssl] ssl_ca_file, ssl_cert_file, ssl_key_file [launch] mode=deferred: wait for pg_autoctl node start [formation N] monitor only: additional named formations ## pg_autoctl node sub-commands run <file> Read ini, create node if absent, exec() into supervisor. Sets PG_AUTOCTL_NODESPEC so the supervisor watches for live [settings] changes via inotify (Linux) or mtime poll. apply <file> Converge mutable settings on an already-running node. start [<file>] Clear launch=deferred so a waiting node run proceeds. show Dump live config as pg_autoctl_node.ini on stdout. check <file> Parse-only validate; print resolved fields. ## Supervisor file watcher (nodespec_watcher) The supervisor initialises a NodeSpecWatcher when PG_AUTOCTL_NODESPEC is set. Every tick it checks for file changes: - Linux: drain inotify IN_CLOSE_WRITE / IN_MOVED_TO events - Others: stat() every NODESPEC_WATCH_INTERVAL_SECS (10 s) On change, re-parse [settings] and call nodespec_apply() to converge mutable fields without restarting the node. ## Files src/bin/pg_autoctl/cli_node.c / cli_node.h src/bin/pg_autoctl/nodespec.c / nodespec.h src/bin/pg_autoctl/supervisor.c / supervisor.h (watcher integration) docs/ref/pg_autoctl_node.rst docs/ref/pg_autoctl_node_run.rst * docs,feat: refine pg_autoctl node docs and make ssl mutable - pg_autoctl_node.rst: replace prose property list with a structured table (section/property/type/mutable/default/description), each property on its own row; fix 'disabled-monitor' label → actual ref - nodespec_apply: add ssl apply block — when [options].ssl or any [ssl] cert path changes, call 'pg_autoctl enable ssl' with the appropriate flags (--ssl-self-signed, --no-ssl, or --ssl-mode + cert paths); update nodespec_apply comment to list ssl as mutable - docs/index.rst: remove 'Container and Kubernetes' as a top-level toctree caption; the manual page is still reachable via Manual Pages - docs/operations.rst: add 'Container and Kubernetes Deployments' section describing pg_autoctl node run, live reconfiguration, and the launch=deferred pattern; cross-refs to pg_autoctl_node for details - docs/ref/configuration.rst: add 'Declarative Node Configuration' section covering pg_autoctl_node.ini sections and their relationship to pg_autoctl.cfg; cross-ref to pg_autoctl_node * nodespec: make monitor_pguri mutable via disable/enable monitor When the [monitor] pguri changes in pg_autoctl_node.ini, nodespec_apply now re-registers the node to the new monitor without stopping Postgres: pg_autoctl disable monitor --force --pgdata <dir> pg_autoctl enable monitor <new_uri> --pgdata <dir> The disable step removes the node from the old monitor (--force allows this even if the old monitor is temporarily unreachable). The enable step registers the node to the new monitor and signals the running supervisor to start using the new monitor_pguri for node_active calls. Document the change in the property table and Live Reconfiguration section of pg_autoctl_node.rst. * docs: complete pg_autoctl node sub-command pages, remove Container section Follow the pg_autoctl_create pattern: each pg_autoctl node sub-command now has its own manual page, all listed in pg_autoctl_node.rst's toctree: pg_autoctl node run (existing) pg_autoctl node apply (new) pg_autoctl node start (new) pg_autoctl node show (new) pg_autoctl node check (new) Remove the redundant 'Container and Kubernetes Deployments' section from operations.rst. The full documentation for this feature lives in the pg_autoctl_node manual page (ref/pg_autoctl_node.rst) and its sub-pages. * docs: replace list-table with definition-list style in pg_autoctl_node The list-table rendered poorly in the Sphinx HTML theme. Replace it with the same definition-list style used throughout the other manual pages (e.g. pg_autoctl_create_postgres Options section): each property is a bare term followed by indented description paragraphs, grouped under ini-section sub-headings. * docs: restore Container/K8s section in operations.rst, note in Provisioning Re-add the 'Container and Kubernetes Deployments' section at the end of operations.rst with a named anchor so it can be referenced from elsewhere. Add a short note in the Provisioning section pointing to pg_autoctl node run as the declarative alternative for container and Kubernetes deployments, with a cross-reference down to the new section. * docs: add pg_autoctl node cross-references throughout the manual Add references to pg_autoctl node / pg_autoctl node run in all the places identified by the documentation audit: Reference pages (new See Also section): - pg_autoctl_create_postgres.rst - pg_autoctl_create_monitor.rst - pg_autoctl_create_coordinator.rst - pg_autoctl_create_worker.rst - pg_autoctl_run.rst Narrative docs (short note pointing to the declarative alternative): - how-to.rst — after the pg_autoctl run step in Quick Start - tutorial.rst — after the docker-compose.yml literalinclude - citus-quickstart.rst — after the docker-compose-scale.yml literalinclude - install.rst — after the systemd unit section * docs: rewrite tutorial to use pg_autoctl node run Replace the imperative pg_autoctl create postgres / pg_autoctl create monitor commands with the declarative pg_autoctl node run approach: - Add tutorial/ini/monitor.ini and tutorial/ini/postgres.ini — two small ini files that describe the monitor and every data node respectively. Data nodes share one ini file; hostname and name default to the container hostname set by Docker Compose. - Rewrite tutorial/docker-compose.yml: all PG_AUTOCTL_* environment variables are gone; each service bind-mounts its ini file at /etc/pgaf/node.ini and runs 'pg_autoctl node run'. The x-node anchor is now clean — no env vars, single command. - Update tutorial.rst to introduce the ini files before the compose file, explain that pg_autoctl node run handles both create and run, and show that live reconfiguration (candidate_priority change for node3) is done by editing the ini file rather than calling pg_autoctl set. - Replace the stale pg_autoctl_do_tmux_compose_session reference in Next steps with a pointer to the pg_autoctl_node reference and the Container and Kubernetes Deployments section. * docs: clarify candidate_priority change — direct command and ini file paths Show both approaches side by side: - Direct command (pg_autoctl set candidate-priority): immediate, no restart - Declarative ini file: explain that changing docker-compose.yml volumes requires 'docker compose up -d node3' to recreate the container, that pg_autoctl node run applies the ini diff on startup before exec'ing into the supervisor, and that once the dedicated ini file is mounted any subsequent edits to it are picked up live by the running supervisor. * docs: rewrite Citus tutorial to use pg_autoctl node run Replace all pg_autoctl create coordinator/worker/monitor commands with the declarative pg_autoctl node run approach: New ini files: citus/ini/monitor.ini — kind = monitor citus/ini/coordinator.ini — kind = coordinator, shared by coord0a/coord0b citus/ini/worker.ini — kind = worker, no group (monitor auto-assigns) citus/ini/worker1.ini — kind = worker, group = 1 citus/ini/worker2.ini — kind = worker, group = 2 citus/ini/worker3.ini — kind = worker, group = 3 Rewritten compose files: docker-compose-scale.yml — three services (monitor/coord/worker), each bind-mounts its ini file; all PG_AUTOCTL_* env vars removed docker-compose.yml — named services with per-group worker ini files; YAML anchors kept for coord and per-group worker templates Updated citus-quickstart.rst: - Show all ini files with literalinclude captions before each compose file - Explain that worker.ini without [formation] group triggers monitor auto-assignment (right for --scale), while workerN.ini with group = N pins the pair to a specific shard group (right for named services) - Explain that pg_autoctl node run uses the container hostname as the node name when name is not set in the ini file - Replace stale pg_autoctl_do_tmux_compose_session reference in Next steps with pointer to pg_autoctl_node and container deployments guide * style: apply citus_indent to nodespec.c * style: fix nodespec.c PUSH macro indentation per citus_indent
1 parent c32eb03 commit 80e9573

36 files changed

Lines changed: 2873 additions & 151 deletions

docs/citus-quickstart.rst

Lines changed: 65 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -50,46 +50,55 @@ or run the docker build command directly:
5050
Our first Citus Cluster
5151
-----------------------
5252

53-
To create a cluster we use the following docker compose definition:
53+
Each node in the cluster is described by a ``pg_autoctl_node.ini`` file
54+
bind-mounted into its container. There are three files:
5455

55-
.. literalinclude:: citus/docker-compose-scale.yml
56-
:language: yaml
57-
:emphasize-lines: 5,15,27
58-
:linenos:
56+
.. literalinclude:: citus/ini/monitor.ini
57+
:language: ini
58+
:caption: citus/ini/monitor.ini
5959

60-
To run the full Citus cluster with HA from this definition, we can use the
61-
following command:
60+
.. literalinclude:: citus/ini/coordinator.ini
61+
:language: ini
62+
:caption: citus/ini/coordinator.ini
6263

63-
::
64+
.. literalinclude:: citus/ini/worker.ini
65+
:language: ini
66+
:caption: citus/ini/worker.ini
6467

65-
$ docker compose up --scale coord=2 --scale worker=6
68+
The ``worker.ini`` has no ``[formation] group`` entry. When that field is
69+
absent the monitor assigns each worker to a group automatically — the first
70+
worker to register in a group becomes primary, the second becomes secondary.
71+
This is the right setup for the scaled deploy below, where we ask Docker
72+
Compose to start six worker containers and let the monitor pair them.
73+
74+
The docker compose definition for the scalable cluster is:
6675

67-
The command above starts the services up. The command also specifies a
68-
``--scale`` option that is different for each service. We need:
76+
.. literalinclude:: citus/docker-compose-scale.yml
77+
:language: yaml
78+
:linenos:
6979

70-
- one monitor node, and the default scale for a service is 1,
80+
Every service runs ``pg_autoctl node run`` — creating the node on first
81+
start, resuming on subsequent starts. All ``PG_AUTOCTL_*`` environment
82+
variables are gone; everything lives in the ini files.
7183

72-
- one primary Citus coordinator node and one secondary Cituscoordinator
73-
node, which is to say two coordinator nodes,
84+
To run the full Citus cluster with HA from this definition:
7485

75-
- and three Citus worker nodes, each worker with both a primary Postgres
76-
node and a secondary Postgres node, so that's a scale of 6 here.
86+
::
7787

78-
The default policy for the pg_auto_failover monitor is to assign a primary
79-
and a secondary per auto failover :ref:`group`. In our case, every node
80-
being provisioned with the same command, we benefit from that default policy::
88+
$ docker compose up --scale coord=2 --scale worker=6
8189

82-
$ pg_autoctl create worker --ssl-self-signed --auth trust --pg-hba-lan --run
90+
The ``--scale`` options tell Docker Compose how many containers to start for
91+
each service:
8392

84-
When provisioning a production cluster, it is often required to have a
85-
better control over which node participates in which group, then using the
86-
``--group N`` option in the ``pg_autoctl create worker`` command line.
93+
- one monitor node (default scale is 1),
94+
- two coordinator containers — one primary, one secondary,
95+
- six worker containers — the monitor pairs them into three groups of two,
96+
assigning a primary and secondary in each group.
8797

88-
Within a given group, the first node that registers is a primary, and the
89-
other nodes are secondary nodes. The monitor takes care of that in a way
90-
that we don't have to. In a High Availability setup, every node should be
91-
ready to be promoted primary at any time, so knowing which node in a group
92-
is assigned primary first is not very interesting.
98+
Within a given group the first node that registers becomes primary; the
99+
monitor handles the assignment so we don't have to track it. In a High
100+
Availability setup every node must be ready for promotion at any time, so
101+
the initial primary assignment within a group is not significant.
93102

94103
While the cluster is being provisionned by docker compose, you can run the
95104
following command and have a dynamic dashboard to follow what's happening.
@@ -177,26 +186,36 @@ more complex docker compose file than in the previous section.
177186

178187
pg_auto_failover architecture with a Citus formation
179188

180-
This time we create a cluster using the following docker compose definition:
189+
This time we need per-group worker ini files so that each worker pair
190+
lands in the right Citus shard group:
191+
192+
.. literalinclude:: citus/ini/worker1.ini
193+
:language: ini
194+
:caption: citus/ini/worker1.ini (worker1a and worker1b)
195+
196+
Worker 2 and worker 3 are identical except for ``group = 2`` and
197+
``group = 3`` respectively. When ``group`` is set, ``pg_autoctl node run``
198+
passes ``--group N`` to ``pg_autoctl create worker``, pinning the pair to
199+
that shard group.
200+
201+
The docker compose definition is:
181202

182203
.. literalinclude:: citus/docker-compose.yml
183204
:language: yaml
184-
:emphasize-lines: 3,15,40,44,48,52,56,60,64,68
185205
:linenos:
186206

187-
This definition is a little more involved than the previous one. We take
188-
benefit from `YAML anchors and aliases`__ to define a *template* for our
189-
coordinator nodes and worker nodes, and then apply that template to the
190-
actual nodes.
207+
We use `YAML anchors and aliases`__ to define templates for the coordinator
208+
and each worker group, then apply them to the named services. Each service
209+
sets its own ``hostname:`` — ``pg_autoctl node run`` uses the container
210+
hostname as the node name when ``name`` is not set in the ini file.
191211

192212
__ https://yaml101.com/anchors-and-aliases/
193213

194-
Also this time we provision an application service (named "app") that sits
195-
in the background and allow us to later connect to our current primary
196-
coordinator. See :download:`Dockerfile.app <citus/Dockerfile.app>` for the
197-
complete definition of this service.
214+
Also this time we provision an application service (``app``) that sits in
215+
the background and allows us to connect to the current primary coordinator.
216+
See :download:`Dockerfile.app <citus/Dockerfile.app>` for its definition.
198217

199-
We start this cluster with a simplified command line this time:
218+
We start this cluster with:
200219

201220
::
202221

@@ -474,13 +493,15 @@ makes it simple to introduce faults and see how the pg_auto_failover High
474493
Availability reacts to those faults.
475494

476495
One obvious missing element to better test the system is the lack of
477-
persistent volumes in our docker compose based test rig. It is possible to
496+
persistent volumes in our docker compose based test rig. It is possible to
478497
create external volumes and use them for each node in the docker compose
479-
definition. This allows restarting nodes over the same data set.
498+
definition, allowing nodes to restart over the same data set.
480499

481-
See the command :ref:`pg_autoctl_do_tmux_compose_session` for more details
482-
about how to run a docker compose test environment with docker compose,
483-
including external volumes for each node.
500+
For production Kubernetes deployments, the same ini files work as
501+
ConfigMaps: bind-mount them alongside a persistent volume claim for
502+
``/tmp/pgaf`` and use ``pg_autoctl node run`` as the container command.
503+
See :ref:`pg_autoctl_node` for the full property reference and
504+
:ref:`container-and-kubernetes-deployments` for production patterns.
484505

485506
Now is a good time to go read `Citus Documentation`__ too, so that you know
486507
how to use this cluster you just created!
Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,34 @@
1-
version: "3.9" # optional since v1.27.0
2-
31
services:
42

53
monitor:
64
image: pg_auto_failover:citus
7-
environment:
8-
PGDATA: /tmp/pgaf
9-
command: |
10-
pg_autoctl create monitor --ssl-self-signed --auth trust --run
5+
volumes:
6+
- /tmp/pgaf
7+
- ./ini/monitor.ini:/etc/pgaf/node.ini:ro
118
expose:
129
- 5432
10+
command: pg_autoctl node run
1311

1412
coord:
1513
image: pg_auto_failover:citus
14+
volumes:
15+
- /tmp/pgaf
16+
- ./ini/coordinator.ini:/etc/pgaf/node.ini:ro
1617
environment:
17-
PGDATA: /tmp/pgaf
1818
PGUSER: citus
1919
PGDATABASE: citus
20-
PG_AUTOCTL_MONITOR: "postgresql://autoctl_node@monitor/pg_auto_failover"
2120
expose:
2221
- 5432
23-
command: |
24-
pg_autoctl create coordinator --ssl-self-signed --auth trust --pg-hba-lan --run
22+
command: pg_autoctl node run
2523

2624
worker:
2725
image: pg_auto_failover:citus
26+
volumes:
27+
- /tmp/pgaf
28+
- ./ini/worker.ini:/etc/pgaf/node.ini:ro
2829
environment:
29-
PGDATA: /tmp/pgaf
3030
PGUSER: citus
3131
PGDATABASE: citus
32-
PG_AUTOCTL_MONITOR: "postgresql://autoctl_node@monitor/pg_auto_failover"
3332
expose:
34-
- 5432
35-
command: |
36-
pg_autoctl create worker --ssl-self-signed --auth trust --pg-hba-lan --run
33+
- 5432
34+
command: pg_autoctl node run

docs/citus/docker-compose.yml

Lines changed: 44 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,14 @@
11
x-coord: &coordinator
22
image: pg_auto_failover:citus
3+
volumes:
4+
- /tmp/pgaf
5+
- ./ini/coordinator.ini:/etc/pgaf/node.ini:ro
36
environment:
4-
PGDATA: /tmp/pgaf
57
PGUSER: citus
68
PGDATABASE: citus
7-
PG_AUTOCTL_HBA_LAN: true
8-
PG_AUTOCTL_AUTH_METHOD: "trust"
9-
PG_AUTOCTL_SSL_SELF_SIGNED: true
10-
PG_AUTOCTL_MONITOR: "postgresql://autoctl_node@monitor/pg_auto_failover"
11-
expose:
12-
- 5432
13-
14-
x-worker: &worker
15-
image: pg_auto_failover:citus
16-
environment:
17-
PGDATA: /tmp/pgaf
18-
PGUSER: citus
19-
PGDATABASE: citus
20-
PG_AUTOCTL_HBA_LAN: true
21-
PG_AUTOCTL_AUTH_METHOD: "trust"
22-
PG_AUTOCTL_SSL_SELF_SIGNED: true
23-
PG_AUTOCTL_MONITOR: "postgresql://autoctl_node@monitor/pg_auto_failover"
249
expose:
2510
- 5432
11+
command: pg_autoctl node run
2612

2713
services:
2814
app:
@@ -40,58 +26,71 @@ services:
4026

4127
monitor:
4228
image: pg_auto_failover:citus
43-
environment:
44-
PGDATA: /tmp/pgaf
45-
PG_AUTOCTL_SSL_SELF_SIGNED: true
29+
volumes:
30+
- /tmp/pgaf
31+
- ./ini/monitor.ini:/etc/pgaf/node.ini:ro
4632
expose:
4733
- 5432
48-
command: |
49-
pg_autoctl create monitor --auth trust --run
34+
command: pg_autoctl node run
5035

5136
coord0a:
5237
<<: *coordinator
5338
hostname: coord0a
54-
command: |
55-
pg_autoctl create coordinator --name coord0a --run
5639

5740
coord0b:
5841
<<: *coordinator
5942
hostname: coord0b
60-
command: |
61-
pg_autoctl create coordinator --name coord0b --run
6243

6344
worker1a:
64-
<<: *worker
45+
<<: &worker1
46+
image: pg_auto_failover:citus
47+
volumes:
48+
- /tmp/pgaf
49+
- ./ini/worker1.ini:/etc/pgaf/node.ini:ro
50+
environment:
51+
PGUSER: citus
52+
PGDATABASE: citus
53+
expose:
54+
- 5432
55+
command: pg_autoctl node run
6556
hostname: worker1a
66-
command: |
67-
pg_autoctl create worker --group 1 --name worker1a --run
6857

6958
worker1b:
70-
<<: *worker
59+
<<: *worker1
7160
hostname: worker1b
72-
command: |
73-
pg_autoctl create worker --group 1 --name worker1b --run
7461

7562
worker2a:
76-
<<: *worker
63+
<<: &worker2
64+
image: pg_auto_failover:citus
65+
volumes:
66+
- /tmp/pgaf
67+
- ./ini/worker2.ini:/etc/pgaf/node.ini:ro
68+
environment:
69+
PGUSER: citus
70+
PGDATABASE: citus
71+
expose:
72+
- 5432
73+
command: pg_autoctl node run
7774
hostname: worker2a
78-
command: |
79-
pg_autoctl create worker --group 2 --name worker2a --run
8075

8176
worker2b:
82-
<<: *worker
77+
<<: *worker2
8378
hostname: worker2b
84-
command: |
85-
pg_autoctl create worker --group 2 --name worker2b --run
8679

8780
worker3a:
88-
<<: *worker
81+
<<: &worker3
82+
image: pg_auto_failover:citus
83+
volumes:
84+
- /tmp/pgaf
85+
- ./ini/worker3.ini:/etc/pgaf/node.ini:ro
86+
environment:
87+
PGUSER: citus
88+
PGDATABASE: citus
89+
expose:
90+
- 5432
91+
command: pg_autoctl node run
8992
hostname: worker3a
90-
command: |
91-
pg_autoctl create worker --group 3 --name worker3a --run
9293

9394
worker3b:
94-
<<: *worker
95+
<<: *worker3
9596
hostname: worker3b
96-
command: |
97-
pg_autoctl create worker --group 3 --name worker3b --run

docs/citus/ini/coordinator.ini

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[node]
2+
kind = coordinator
3+
port = 5432
4+
5+
[postgresql]
6+
pgdata = /tmp/pgaf
7+
8+
[monitor]
9+
pguri = postgresql://autoctl_node@monitor/pg_auto_failover
10+
11+
[options]
12+
ssl = self-signed
13+
auth = trust
14+
pg_hba_lan = true

docs/citus/ini/monitor.ini

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
[node]
2+
kind = monitor
3+
port = 5432
4+
5+
[postgresql]
6+
pgdata = /tmp/pgaf
7+
8+
[options]
9+
ssl = self-signed
10+
auth = trust

docs/citus/ini/worker.ini

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[node]
2+
kind = worker
3+
port = 5432
4+
5+
[postgresql]
6+
pgdata = /tmp/pgaf
7+
8+
[monitor]
9+
pguri = postgresql://autoctl_node@monitor/pg_auto_failover
10+
11+
[options]
12+
ssl = self-signed
13+
auth = trust
14+
pg_hba_lan = true

docs/citus/ini/worker1.ini

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
[node]
2+
kind = worker
3+
port = 5432
4+
5+
[postgresql]
6+
pgdata = /tmp/pgaf
7+
8+
[monitor]
9+
pguri = postgresql://autoctl_node@monitor/pg_auto_failover
10+
11+
[formation]
12+
group = 1
13+
14+
[options]
15+
ssl = self-signed
16+
auth = trust
17+
pg_hba_lan = true

0 commit comments

Comments
 (0)