Re: Enhance 'pg_createsubscriber' to retrieve databases automatically when no database is provided.

From: Peter Smith <smithpb2250(at)gmail(dot)com>
To: Shubham Khanna <khannashubham1197(at)gmail(dot)com>
Cc: Ashutosh Bapat <ashutosh(dot)bapat(dot)oss(at)gmail(dot)com>, Amit Kapila <amit(dot)kapila16(at)gmail(dot)com>, PostgreSQL Hackers <pgsql-hackers(at)lists(dot)postgresql(dot)org>
Subject: Re: Enhance 'pg_createsubscriber' to retrieve databases automatically when no database is provided.
Date: 2025-02-06 04:08:40
Message-ID: CAHut+PuyBsOJTSygus2-yp60sw_phwYQ-JyC+U6fCBMos9x2LA@mail.gmail.com
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

Hi Shubham.

Here are some review comments for v3-0001.

FYI, I skipped the review of the test code because I saw Kuroda-san
had already posted some comments about the tests

======
Commit message

1.
This patch enhances the 'pg_createsubscriber' utility to automatically
fetch all non-template databases from the publisher and create subscriptions
for them. This simplifies converting a physical standby to a logical subscriber
for multiple databases, particularly during upgrades.

~

AFAIK you are not creating the subscription at the publisher database,
which is what this description sounds like it is saying. I think you
are creating the subscriptions on the target server on the *same name*
databases that you found on the source server (???). Anyway, this
needs clarification. Also, in some of the comments below, I had the
same confusion.

~~~

2.
A new function 'fetch_all_databases' in 'pg_createsubscriber.c'
that queries the publisher for all databases and adds them to the subscription
options is added.
Additionally, 'validate_databases' ensures that conflicting options are not
used together, enforcing correct usage of '--all-databases'.

~

IMO it is better here to describe the overall logic/algorithms rather
than saying "This new function does this, and this other new function
does that". (otherwise, your commit message will quickly become stale
and need changing all the time).

~~~

3.
The '--all-databases' option fetches all databases from the publisher.
It auto-generates publication and replication slot names as 'pub_names' and
'slot_names' respectively.

~

'pub_names'?
'slot_names'?

What are those strings? Those are not the names that you wrote in the document.

~~~

4.
This option validates database name lengths to ensure generated names fit
within system limits.

~

No. Maybe the patch code ensures this (but I didn't find this
validation anywhere), but certainly "This option" doesn't.

======
doc/src/sgml/ref/pg_createsubscriber.sgml

5.
The synopsis has no mention of --all-databases.

I'm not sure of the best way to do it -- if it becomes too messy to
combine everything then maybe having several is the way to go:

e.g.

pg_createsubscriber [option...] { -a | --all-databases } { -D |
--pgdata }datadir { -P | --publisher-server }connstr
pg_createsubscriber [option...] { -d | --database }dbname { -D |
--pgdata }datadir { -P | --publisher-server }connstr

~~~

6.
+ <para>
+ Automatically fetch all non-template databases from the publisher and
+ create subscriptions for them.
+ When using <option>--all-databases</option>, publication and
+ replication slot names will be automatically generated in the format
+ <literal><replaceable>database</replaceable>_pub</literal> and
+ <literal><replaceable>database</replaceable>_slot</literal>
respectively.
+ When using <option>-a</option>, ensure database names are shorter
+ than 59 characters to allow for generated publication and slot names.
+ replica.
+ </para>

6a.
"fetch all non-template databases from the publisher and create
subscriptions for them" -- Hmm. It's too confusing. I guess you meant
that you are enumerating all the databases on the source server but
then creating subscriptions on all the databases at the target server
that have the same name (???) Anyway, the current text is not clear. I
also think it is better to use the "target server" and "source server"
(aka publication-server) terminology.

~

6b.
I don't think you need to say "When using
<option>--all-databases</option>," because this entire description for
'--all-databases'

~

6c.
Talks about publication and slot names but no mention of subscription names?

~

6d.
That limitation about the 59 char limit seems a strange thing to have
documented here. I wonder why you need to say anything at all. It
might be better if the code just validates all this up-front before
processing and can raise an ERROR in the very rare times this might
happen.

~

6e.
Spurious text junk? "replica"

~

6f.
Don't you need to also say something like "Cannot be used with
'--publication', '--subscription', or '--replication-slot'."

~~~

7. --database

- switches.
+ switches. If <option>-d</option> and <option>-a</option> are specified
+ together, <application>pg_createsubscriber</application> will return
+ an error.

A nicer way to say that would be to spell it out fully. e.g. "Cannot
be used with --all-databases'.".

~~~

8.
For --publication don't you need to mention "Cannot be used with
--all-databases'.".

~~~

9.
For --subscription don't you need to mention "Cannot be used with
--all-databases'.".

~~~

10.
For --replication-slot don't you need to mention "Cannot be used with
--all-databases'.".

======
src/bin/pg_basebackup/pg_createsubscriber.c

usage:

11.
printf(_("\nOptions:\n"));
+ printf(_(" -a, --all-databases fetch and specify all
databases\n"));

This doesn't really seem a helpful usage text. Should it say something
more like "create subscriptions for all databases"... (??) **

** I have difficulty imagining what is the expected behaviour in cases
where there might be different databases at the source server and
target server. I think your documentation needs to have some special
notes about this to make it clear what server you are enumerating
these databases on, and then what happens if there is some mismatch of
what databases are available on the *other* server.

~~~

fetch_all_databases:

12.
+/* Placeholder function to fetch all databases from the publisher */
+static void
+fetch_all_databases(struct CreateSubscriberOptions *opt)
+{

12a.
What is a "placeholder function"?

~

12b.
I would be nicers to use the same terminology that seems more common
in the documentation -- e.g. "source server" instead of "publisher".
Also, the function name should make it clear if you are getting these
data from the source server or target server.

~~~

13.
+ /* Process the query result */
+ num_rows = PQntuples(res);
+ for (int i = 0; i < num_rows; i++)
+ {
+ const char *dbname = PQgetvalue(res, i, 0);
+
+ simple_string_list_append(&opt->database_names, dbname);
+ num_dbs++;
+ }
The increment num_dbs++ is a bit sneaky. AFAICT you are effectively
pretending --all-databases is the equivalent of a whole lot of
--database so the subsequent logic won't know the difference. I think
that logic deserves a code comment.

~~~

14.
+ /* Check if any databases were added */
+ if (opt->database_names.head == NULL)
+ {
+ pg_log_error("no database names could be fetched or specified");
+ pg_log_error_hint("Ensure that databases exist on the publisher or
specify a database using --database option.");
+ PQclear(res);
+ PQfinish(conn);
+ exit(1);
+ }
+
+ PQclear(res);
+ PQfinish(conn);
+}

14a.
The "were added" seems odd. I think you mean. Just "Error if no
databases were found".

~

14b.
Isn't it simpler just to check if num_dbs == 0?

~

14c.
"no database names could be fetched or specified" seems like a weird
error text. e.g. "specified"?

~

14d.
The hint seems strange to me. If there are no databases found using
--all-database then how will "specify a database using --database
option." help the user get a better result?

~~~

validate_databases:

15.
+static void
+validate_databases(struct CreateSubscriberOptions *opt)
+{
+ /* Check for conflicting options */
+ if (opt->all_databases && opt->database_names.head != NULL)
+ {
+ pg_log_error("cannot specify --dbname when using --all-databases");
+ exit(1);
+ }
+
+ /*
+ * Ensure publication and slot names are not manually specified with
+ * --all-databases
+ */
+ if (opt->all_databases &&
+ (opt->pub_names.head != NULL || opt->replslot_names.head != NULL))
+ {
+ pg_log_error("cannot specify --publication or --replication-slot
when using --all-databases");
+ exit(1);
+ }
+

I think all this switch-checking is misplaced. It should be happening
up-front in the main function.

~~~

16.
+ /* Auto-generate publication and slot names for all databases */
+ if (opt->all_databases)
+ {
+ SimpleStringListCell *cell;
+
+ fetch_all_databases(opt);
+
+ cell = opt->database_names.head;
+
+ while (cell != NULL)
+ {
+ char slot_name[NAMEDATALEN];
+
+ snprintf(slot_name, sizeof(slot_name), "%s_slot", cell->val);
+ simple_string_list_append(&opt->replslot_names, slot_name);
+
+ snprintf(slot_name, sizeof(slot_name), "%s_pub", cell->val);
+ simple_string_list_append(&opt->pub_names, slot_name);
+
+ cell = cell->next;
+ }
+ }

16a.
Why is this code checking 'opt->all_databases'? Isn't it only possible
to get to this function via --all-databases. You can just Assert this.

~

16b.
Why are you reusing 'slot_name' variable like this?

~

16c.
Was there some ERROR checking missing here to ensure the length of the
dbname is not going to cause pub/slot to exceed NAMEDATALEN?

~

16d.
In hindsight, most of this function seems unnecessary to me. Probably
you could've done all this pub/slot name assignment inside the
fetch_all_databases() at the same time as you were doing
simple_string_list_append(&opt->database_names, dbname); for each
database.

~~~

17.
- while ((c = getopt_long(argc, argv, "d:D:np:P:s:t:U:v",
+ while ((c = getopt_long(argc, argv, "ad:D:np:P:s:t:U:v",
long_options, &option_index)) != -1)

Isn't the code within this loop missing all the early switch
validation for checking you are not combining incompatible switches?

e.g.
--all-databases - "Cannot specify --all-databases more than once"
--all-databases - "Cannot combine with --database, --publication,
--subscription, --replication-slot"
--database - "Cannot combine with --all-databases"
--publication - "Cannot combine with --all-databases"
--subscription - "Cannot combine with --all-databases"
--replication-slot - "Cannot combine with --all-databases"

~~~

18.
+
+
/* Number of object names must match number of databases */

Remove spurious whitespace.

======
Kind Regards,
Peter Smith.
Fujitsu Australia

In response to

Browse pgsql-hackers by date

  From Date Subject
Next Message Jeff Davis 2025-02-06 04:45:06 Re: Statistics Import and Export
Previous Message Corey Huinker 2025-02-06 04:01:13 Re: Statistics Import and Export