File Coverage

blib/lib/PGObject/Util/Replication/Master.pm
Criterion Covered Total %
statement 21 70 30.0
branch 0 8 0.0
condition 0 6 0.0
subroutine 8 20 40.0
pod 11 11 100.0
total 40 115 34.7


line stmt bran cond sub pod time code
1             package PGObject::Util::Replication::Master;
2              
3 5     5   103613 use 5.006;
  5         21  
4 5     5   32 use strict;
  5         12  
  5         126  
5 5     5   27 use warnings;
  5         17  
  5         143  
6              
7 5     5   2451 use Moo;
  5         57641  
  5         25  
8 5     5   13772 use DBI;
  5         74290  
  5         632  
9 5     5   2619 use PGObject::Util::PGConfig;
  5         5159  
  5         136  
10 5     5   2042 use PGObject::Util::Replication::Slot;
  5         114265  
  5         4072  
11              
12             =head1 NAME
13              
14             PGObject::Util::Replication::Master - Manage Database Masters
15              
16             =head1 VERSION
17              
18             Version 0.01
19              
20             =cut
21              
22             our $VERSION = 'v0.01';
23              
24             =head1 SYNOPSIS
25              
26             Quick summary of what the module does.
27              
28             Perhaps a little code snippet.
29              
30             use PGObject::Util::Replication::Master;
31              
32             my $masterdb = PGObject::Util::Replication::Master->new(
33             host => 'localhost',
34             port => 5432,
35             );
36              
37             # also slot management
38             $master->slots();
39             $master->addslot('slotname');
40             $master->removeslot('slotname');
41              
42            
43             =head1 SERVER PROPERTIES
44              
45             =head2 host
46              
47             Hostname or IP address
48              
49             =head2 port
50              
51             defaults to 5432
52              
53             =head2 user
54              
55             Username for the connection string. If not provided, dbi defaults to the
56             username for the system user of the script running this module
57              
58             =head2 password
59              
60             Password for connecting, if needed based on pg_hba.conf settings
61              
62             =head2 dbname
63              
64             Database name for connecting. Defaults to postgres
65              
66             =head2 autocommit
67              
68             Whether to autocommit statements. This defaults to true.
69              
70             =head2 persist_connect
71              
72             If set, save the connection for repeated use. Defaults to off
73             which means we reconnect for each operation.
74              
75             Set to true if you are doing a lot of operations.
76              
77             =head2 manage_vars
78              
79             This is the variables we read or write. It is intended to be unmodified as it is.
80              
81             =cut manage_vars
82              
83             has host => (is => 'ro');
84             has port => (is => 'ro', default => 5432);
85             has user => (is => 'ro');
86             has password => (is => 'ro');
87             has dbname => (is => 'ro', default => 'postgres');
88             has autocommit => (is => 'ro', default => 1);
89             has persist_connect => (is => 'ro', default => 0);
90             has manage_vars => (is => 'rw', default => sub { _manage_vars() } );
91             has config => (is => 'lazy');
92              
93             sub _manage_vars {
94 2     2   41 return [qw(
95             wal_level fsync synchronous_commit synchronous_standby_names
96             wal_sync_method full_page_writes wal_log_hints wal_compression
97             wal_buffers archive_mode archive_command archive_timeout
98             max_wal_senders max_replication_slots wal_keep_segments
99             wal_sender_timeout track_commit_timestamps
100             )];
101             }
102             sub _build_config {
103 0     0     my ($self) = @_;
104 0           return PGObject::Util::PGConfig->new($self->manage_vars, $self->connect);
105             }
106              
107             =head1 METHDOS
108              
109             =head2 connect
110              
111             Returns a DBI connection to the database and checks to make sure
112             we are not in recovery. If we are in recovery, an exception is thrown.
113              
114             =head2 disconnect
115              
116             Disconnect, if using persist_connect
117              
118             =cut
119              
120             {
121              
122             my $pdbh;
123             sub connect {
124 0     0 1   my ($self) = @_;
125 0 0 0       return $pdbh if $pdbh and $self->persist_connect;
126 0           my $dbh = DBI->connect("dbi:Pg:dbname=" . $self->dbname, $self->user, $self->password);
127 0           my $sth = $dbh->prepare('select pg_is_in_recovery()');
128 0           $sth->execute;
129 0 0         die 'Database Is Recovering' if ($sth->fetchrow_array)[0];
130 0 0         $pdbh = $dbh if $self->persist_connect;
131 0           return $dbh;
132             }
133             sub disconnect {
134 0 0 0 0 1   $pdbh->disconnect if $pdbh and $pdbh->can('disconnect');
135 0           undef $pdbh;
136             }
137              
138             }
139              
140              
141             =head2 can_manage
142              
143             Returns true if the user credentials allow us to create and manage replication slots.
144              
145             This requires either superuser or replication attributes for the user.
146              
147             =cut
148              
149             sub can_manage {
150 0     0 1   my ($self) = @_;
151 0           my $dbh = $self->connect();
152 0           my $sth = $dbh->prepare('select rolsuper or rolreplication from pg_roles where rolname = session_user');
153 0           $sth->execute;
154 0           return ($sth->fetchrow_array())[0];
155             }
156              
157             =head2 slots([prefix])
158              
159             Lists all replication slots, their lags, etc.
160              
161             =cut
162              
163             sub slots {
164 0     0 1   my ($self, $filter) = @_;
165 0           return PGObject::Util::Replication::Slot->all($self->connect, $filter);
166             }
167              
168             =head2 getslot($name)
169              
170             Returns info from a single named slot
171              
172             =cut
173              
174             sub getslot {
175 0     0 1   my ($self, $name) = @_;
176 0           return PGObject::Util::Replication::Slot->get($self->connect, $name);
177             }
178              
179             =head2 addslot
180              
181             Adds a named replication slot
182              
183             =cut
184              
185             sub addslot {
186 0     0 1   my ($self, $name) = @_;
187 0           return PGObject::Util::Replication::Slot->create($self->connect, $name);
188             }
189              
190             =head2 deleteslot($name)
191              
192             Deletes a named replication slot
193              
194             =cut
195              
196             sub deleteslot {
197 0     0 1   my ($self, $name) = @_;
198 0           return PGObject::Util::Replication::Slot->delete($self->connect, $name);
199             }
200              
201             =head2 ping_wal()
202              
203             connects to the db and asks for the current wal log series
204             number (lsn) and related information. Is intended to be
205             used for monitoring and to deliver telemtry data.
206              
207             Returned hashref includes:
208              
209             =over
210              
211             =item snapshot_lsn
212              
213             The curret query log sequence number at the time of snapshot epoch
214              
215             =item snapshpt_epoch
216              
217             The current epoch at time of snapshot (technically small variance
218             is possible here).
219              
220             =item delta_bytes
221              
222             Number of bytes sent to the WAL since last time this was called
223             (if known). Will be undef on first run after loading module.
224              
225             =item delta_secs
226              
227             Number of seconds (including fractional seconds) since last run
228             if known. Will be undef on first run after loading module.
229              
230             =item bytes_per_sec
231              
232             Number of bytes per sec on average since last run. Will be undef
233             on first run.
234              
235             =back
236              
237             On the first run, we will get the lsn and the epoch of the
238             current snapshot but no deltas because we lack a comparison.
239             On subsequent runs we get telemetry.
240              
241             =cut
242              
243             {
244             my $last = {
245             snapshot_lsn => undef,
246             snapshot_epoch => undef,
247             delta_bytes => undef,
248             delta_secs => undef,
249             bytes_per_sec => undef,
250             };
251             sub ping_wal {
252 0     0 1   my ($self) = @_;
253 0           my $dbh = $self->connect;
254 0           my $sth = $dbh->prepare("
255             WITH snapshot AS (
256             SELECT pg_current_xlog_location() AS snapshot_lsn,
257             EXTRACT(EPOCH FROM clock_timestamp()) AS snapshot_epoch
258             ), old AS (select ?::pg_lsn as last_lsn, ?::numeric last_epoch)
259             SELECT snapshot.*, snapshot_lsn - last_lsn AS delta_bytes,
260             snapshot_epoch - last_epoch as delta_secs,
261             (snapshot_lsn - last_lsn) / (snapshot_epoch - last_epoch) AS bytes_per_sec
262             FROM snapshot cross join old;
263             ");
264 0           $sth->execute($last->{snapshot_lsn}, $last->{snapshot_epoch});
265 0           my $current = $sth->fetchrow_hashref('NAME_lc');
266 0           $last = $current;
267 0           return $current;
268             }
269             }
270              
271             =head2 readconfig
272              
273             Reads all settings from the pg instance.
274              
275             =head2 addconfig
276              
277             Adds one more config setting to the managed list and sets it to the default
278              
279             =head2 setconfig
280              
281             Sets a config value for later config file writing.
282              
283             =head2 configcontents
284              
285             Returns a file of the structure of the config file, based on all managed values.
286              
287             =cut
288              
289             sub readconfig {
290 0     0 1   my ($self) = @_;
291 0           my $dbh = $self->connect;
292 0           my $sth = $dbh->prepare("
293             SELECT name, current_setting(name)
294             FROM pg_settings
295             WHERE name = any(?)");
296 0           $sth->execute($self->manage_vars);
297 0           my @outlist = ();
298 0           my @next;
299 0           push @outlist, @next while @next = $sth->fetchrow_array;
300 0           my %vars = @outlist;
301 0           $self->config->set($_, $vars{$_}) for keys %vars;
302              
303             }
304              
305             sub addconfig {
306 0     0 1   my ($self, $name);
307 0           my $manage_ref = $self->manage_vars;
308 0           push @$manage_ref,$name;
309 0           my $sth = $self->connect->prepare("select current_setting(?)");
310 0           $sth->execute($name);
311 0           $self->config->update({$name => $sth->fetchrow_array});
312             }
313              
314             sub configcontents {
315 0     0 1   my ($self) = @_;
316 0           return $self->config->filecontents();
317             }
318              
319              
320              
321             =head1 AUTHOR
322              
323             Chris Travers, C<< >>
324              
325             =head1 BUGS
326              
327             Please report any bugs or feature requests to C, or through
328             the web interface at L. I will be notified, and then you'll
329             automatically be notified of progress on your bug as I make changes.
330              
331              
332              
333              
334             =head1 SUPPORT
335              
336             You can find documentation for this module with the perldoc command.
337              
338             perldoc PGObject::Util::Replication::Master
339              
340              
341             You can also look for information at:
342              
343             =over 4
344              
345             =item * RT: CPAN's request tracker (report bugs here)
346              
347             L
348              
349             =item * AnnoCPAN: Annotated CPAN documentation
350              
351             L
352              
353             =item * CPAN Ratings
354              
355             L
356              
357             =item * Search CPAN
358              
359             L
360              
361             =back
362              
363              
364             =head1 ACKNOWLEDGEMENTS
365              
366              
367             =head1 LICENSE AND COPYRIGHT
368              
369             Copyright 2017 Chris Travers.
370              
371             This program is distributed under the (Revised) BSD License:
372             L
373              
374             Redistribution and use in source and binary forms, with or without
375             modification, are permitted provided that the following conditions
376             are met:
377              
378             * Redistributions of source code must retain the above copyright
379             notice, this list of conditions and the following disclaimer.
380              
381             * Redistributions in binary form must reproduce the above copyright
382             notice, this list of conditions and the following disclaimer in the
383             documentation and/or other materials provided with the distribution.
384              
385             * Neither the name of Adjust.com
386             nor the names of its contributors may be used to endorse or promote
387             products derived from this software without specific prior written
388             permission.
389              
390             THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
391             "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
392             LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
393             A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
394             OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
395             SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
396             LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
397             DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
398             THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
399             (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
400             OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
401              
402              
403             =cut
404              
405             1; # End of PGObject::Util::Replication::Master