Merge lp:~stub/charms/trusty/postgresql/standby-service into lp:~stub/charms/trusty/postgresql/trunk

Proposed by Stuart Bishop
Status: Superseded
Proposed branch: lp:~stub/charms/trusty/postgresql/standby-service
Merge into: lp:~stub/charms/trusty/postgresql/trunk
Diff against target: 287 lines (+122/-20)
4 files modified
config.yaml (+5/-5)
hooks/hooks.py (+103/-11)
metadata.yaml (+11/-1)
templates/postgresql.conf.tmpl (+3/-3)
To merge this branch: bzr merge lp:~stub/charms/trusty/postgresql/standby-service
Reviewer Review Type Date Requested Status
Stuart Bishop Abstain
charmers Pending
Review via email: mp+264401@code.launchpad.net

Description of the change

The last landing broke PostgreSQL < 9.4 installs using the default configuration options.

Fix this by downgrading unsupported wal_level's when necessary. This seems preferable to changing the default, which would mean requiring the wal_level to be set before the new relation type would actually work.

To post a comment you must log in.
Revision history for this message
Stuart Bishop (stub) :
review: Abstain

Unmerged revisions

126. By Stuart Bishop

Handle unsupported wal_level with PG < 9.4

125. By Stuart Bishop

Merge trunk

124. By Stuart Bishop

Bug #1458754 workaround and tweaks

123. By Stuart Bishop

Run config_changed *after* creating the user and db it needs

122. By Stuart Bishop

External logical replication

121. By Stuart Bishop

master relation hooks

120. By Stuart Bishop

Correct relation name

119. By Stuart Bishop

Replica relation for replicas external to the service

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'config.yaml'
2--- config.yaml 2015-03-12 10:18:22 +0000
3+++ config.yaml 2015-07-10 11:09:27 +0000
4@@ -179,13 +179,13 @@
5 transactions on a streaming hot standby and allowing the master to
6 defer cleanup and avoid query cancelations on the hot standby.
7 wal_level:
8- default: minimal
9+ default: logical
10 type: string
11 description: >
12- 'minimal', 'archive' or 'hot_standby'. Defines how much information
13- is written to the WAL. Set to 'minimal' for stand alone databases
14- and 'hot_standby' for replicated setups. Overridden by juju when
15- replication is used.
16+ 'minimal', 'archive', 'hot_standby' or 'logical'. Defines how much
17+ information is written to the WAL. Set to 'minimal' for stand alone
18+ databases and 'hot_standby' for replicated setups. Overridden by
19+ juju when replication is used.
20 max_wal_senders:
21 default: 0
22 type: int
23
24=== modified file 'hooks/hooks.py' (properties changed: -x to +x)
25--- hooks/hooks.py 2015-07-03 06:19:42 +0000
26+++ hooks/hooks.py 2015-07-10 11:09:27 +0000
27@@ -158,6 +158,9 @@
28 for relid in hookenv.relation_ids('replication'):
29 hookenv.relation_set(relid, replication_state)
30
31+ for relid in hookenv.relation_ids('master'):
32+ hookenv.relation_set(relid, state=self.get('state'))
33+
34 log('saving local state', DEBUG)
35 self.save()
36
37@@ -411,6 +414,12 @@
38 host.write_file(postgresql_sysctl, ''.join(lines), perms=0600)
39 _run_sysctl(postgresql_sysctl)
40
41+ # Our config file specifies a default wal_level that only works
42+ # with PostgreSQL 9.4. Downgrade this for earlier versions of
43+ # PostgreSQL. We have this default so more things Just Work.
44+ if pg_version() < '9.4' and config_data['wal_level'] == 'logical':
45+ config_data['wal_level'] = 'hot_standby'
46+
47 # If we are replicating, some settings may need to be overridden to
48 # certain minimum levels.
49 num_slaves = slave_count()
50@@ -418,7 +427,8 @@
51 log('{} hot standbys in peer relation.'.format(num_slaves))
52 log('Ensuring minimal replication settings')
53 config_data['hot_standby'] = True
54- config_data['wal_level'] = 'hot_standby'
55+ if config_data['wal_level'] != 'logical':
56+ config_data['wal_level'] = 'hot_standby'
57 config_data['wal_keep_segments'] = max(
58 config_data['wal_keep_segments'],
59 config_data['replicated_wal_keep_segments'])
60@@ -432,12 +442,14 @@
61 # non-streaming replication, or for PITR.
62 if config_data.get('swiftwal_log_shipping', None):
63 config_data['archive_mode'] = True
64- config_data['wal_level'] = 'hot_standby'
65+ if config_data['wal_level'] != 'logical':
66+ config_data['wal_level'] = 'hot_standby'
67 config_data['archive_command'] = swiftwal_archive_command()
68
69 if config_data.get('wal_e_storage_uri', None):
70 config_data['archive_mode'] = True
71- config_data['wal_level'] = 'hot_standby'
72+ if config_data['wal_level'] != 'logical':
73+ config_data['wal_level'] = 'hot_standby'
74 config_data['archive_command'] = wal_e_archive_command()
75
76 # Send config data to the template
77@@ -565,7 +577,8 @@
78 # every other unit's postgres database and the magic replication
79 # database. It also needs to be able to connect to its own postgres
80 # database.
81- for relid in hookenv.relation_ids('replication'):
82+ relids = hookenv.relation_ids('replication')
83+ for relid in relids:
84 for unit in hookenv.related_units(relid):
85 relation = hookenv.relation_get(unit=unit, rid=relid)
86 remote_addr = munge_address(relation['private-address'])
87@@ -584,7 +597,34 @@
88 }
89 relation_data.append(remote_pgdb)
90
91- # Hooks need permissions too to setup replication.
92+ # More replication connections, this time from external services.
93+ # Somewhat different than before, as we do not share credentials
94+ # and services using 9.4's logical replication feature will want
95+ # to specify the database name.
96+ relids = hookenv.relation_ids('master')
97+ for relid in relids:
98+ for unit in hookenv.related_units(relid):
99+ remote_rel = hookenv.relation_get(unit=unit, rid=relid)
100+ local_rel = hookenv.relation_get(unit=hookenv.local_unit(),
101+ rid=relid)
102+ remote_addr = munge_address(remote_rel['private-address'])
103+ remote_replication = {'database': 'replication',
104+ 'user': local_rel['user'],
105+ 'private-address': remote_addr,
106+ 'relation-id': relid,
107+ 'unit': unit,
108+ }
109+ relation_data.append(remote_replication)
110+ if 'database' in local_rel:
111+ remote_pgdb = {'database': local_rel['database'],
112+ 'user': local_rel['user'],
113+ 'private-address': remote_addr,
114+ 'relation-id': relid,
115+ 'unit': unit,
116+ }
117+ relation_data.append(remote_pgdb)
118+
119+ # Local hooks also need permissions to setup replication.
120 for relid in hookenv.relation_ids('replication'):
121 local_replication = {'database': 'postgres',
122 'user': 'juju_replication',
123@@ -2176,16 +2216,14 @@
124 # Build the set of client relations that both the master and this
125 # unit have joined.
126 possible_client_relations = set(hookenv.relation_ids('db') +
127- hookenv.relation_ids('db-admin'))
128+ hookenv.relation_ids('db-admin') +
129+ hookenv.relation_ids('master'))
130 active_client_relations = possible_client_relations.intersection(
131 set(client_relations.split()))
132
133 for client_relation in active_client_relations:
134 # We need to pull the credentials from the master unit's
135- # end of the client relation. This is problematic as we
136- # have no way of knowing if the master unit has joined
137- # the relation yet. We use the exception handler to detect
138- # this case per Bug #1192803.
139+ # end of the client relation.
140 log('Hot standby republishing credentials from {} to {}'.format(
141 master, client_relation))
142
143@@ -2403,6 +2441,8 @@
144 num_slaves = 0
145 for relid in hookenv.relation_ids('replication'):
146 num_slaves += len(hookenv.related_units(relid))
147+ for relid in hookenv.relation_ids('master'):
148+ num_slaves += len(hookenv.related_units(relid))
149 return num_slaves
150
151
152@@ -2480,7 +2520,7 @@
153 or not metrics_sample_interval):
154 log("Required config not found or invalid "
155 "(metrics_target, metrics_sample_interval), "
156- "disabling metrics", WARNING)
157+ "disabling statsd metrics", DEBUG)
158 delete_metrics_cronjob(cron_path)
159 return
160
161@@ -2691,6 +2731,58 @@
162 postgresql_stop()
163
164
165+@hooks.hook('master-relation-joined', 'master-relation-changed')
166+def master_relation_joined_changed():
167+ local_relation = hookenv.relation_get(unit=hookenv.local_unit())
168+
169+ # Relation settings both master and standbys can set now.
170+ allowed_units = sorted(hookenv.related_units()) # Bug #1458754
171+ hookenv.relation_set(
172+ relation_settings={'allowed-units': ' '.join(allowed_units),
173+ 'host': hookenv.unit_private_ip(),
174+ 'port': get_service_port(),
175+ 'state': local_state['state'],
176+ 'version': pg_version()})
177+
178+ if local_state['state'] == 'hot standby':
179+ # Hot standbys cannot create credentials. Publish them from the
180+ # master if they are available, or defer until a peer-relation-changed
181+ # hook when they are.
182+ publish_hot_standby_credentials()
183+ config_changed()
184+ return
185+
186+ user = local_relation.get('user') or user_name(hookenv.relation_id(),
187+ hookenv.remote_unit())
188+ password = local_relation.get('password') or create_user(user,
189+ admin=True,
190+ replication=True)
191+ hookenv.relation_set(user=user, password=password)
192+
193+ # For logical replication, the standby service may request an explicit
194+ # database.
195+ database = hookenv.relation_get('database')
196+ if database:
197+ ensure_database(user, user, database)
198+ hookenv.relation_set(database=database) # Signal database is ready
199+
200+ # We may need to bump the number of replication connections and
201+ # restart, and we will certainly need to regenerate pg_hba.conf
202+ # and reload.
203+ config_changed() # Must be called after db & user are created.
204+
205+
206+@hooks.hook()
207+def master_relation_departed():
208+ config_changed()
209+ allowed_units = hookenv.relation_get('allowed-units',
210+ hookenv.local_unit()).split()
211+ if hookenv.remote_unit() in allowed_units:
212+ allowed_units.remove(hookenv.remote_unit())
213+ hookenv.relation_set(relation_settings={
214+ 'allowed-units': ' '.join(allowed_units)})
215+
216+
217 def _get_postgresql_config_dir(config_data=None):
218 """ Return the directory path of the postgresql configuration files. """
219 if config_data is None:
220
221=== removed symlink 'hooks/master-relation-broken'
222=== target was u'hooks.py'
223=== added symlink 'hooks/master-relation-departed'
224=== target is u'hooks.py'
225=== modified file 'metadata.yaml'
226--- metadata.yaml 2014-05-29 13:08:35 +0000
227+++ metadata.yaml 2015-07-10 11:09:27 +0000
228@@ -18,8 +18,10 @@
229 provides:
230 db:
231 interface: pgsql
232+ optional: true
233 db-admin:
234 interface: pgsql
235+ optional: true
236 nrpe-external-master:
237 interface: nrpe-external-master
238 scope: container
239@@ -27,6 +29,14 @@
240 interface: block-storage
241 scope: container
242 optional: true
243+ master:
244+ interface: pgreplication
245+ optional: true
246+ # We do not yet support slaving a PostgreSQL service to another
247+ # PostgreSQL service.
248+ # replica:
249+ # interface: pgreplication
250+ # optional: true
251 requires:
252 persistent-storage:
253 interface: directory-path
254@@ -34,4 +44,4 @@
255 interface: syslog
256 peers:
257 replication:
258- interface: pgreplication
259+ interface: pgpeer
260
261=== modified file 'templates/postgresql.conf.tmpl'
262--- templates/postgresql.conf.tmpl 2015-03-12 10:18:22 +0000
263+++ templates/postgresql.conf.tmpl 2015-07-10 11:09:27 +0000
264@@ -184,7 +184,6 @@
265 standard_conforming_strings = {{standard_conforming_strings}}
266 {% endif -%}
267
268-{% if version >= "9.0" -%}
269 #------------------------------------------------------------------------------
270 # Replication
271 #------------------------------------------------------------------------------
272@@ -199,6 +198,9 @@
273 {% endif -%}
274 {% if max_wal_senders != "" -%}
275 max_wal_senders = {{max_wal_senders}}
276+{% if version >= "9.4" -%}
277+max_replication_slots = {{max_wal_senders}}
278+{% endif -%}
279 {% endif -%}
280 {% if wal_keep_segments != "" -%}
281 wal_keep_segments = {{wal_keep_segments}}
282@@ -209,5 +211,3 @@
283 {% if archive_command != "" -%}
284 archive_command = '{{archive_command}}'
285 {% endif -%}
286-
287-{% endif -%}

Subscribers

People subscribed via source and target branches

to all changes: