libpq CREATE DATABASE operation from multiple treads

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
18 messages Options
Reply | Threaded
Open this post in threaded view
|

libpq CREATE DATABASE operation from multiple treads

p.sun.fun
Hi, PostgreSQL community.

I hope everyone is doing great and keep themselves safe and healthy.

I am not sure whether my question should belong here. Please advise if
this is the wrong place.

I am contributing to the libgda project (
https://gitlab.gnome.org/GNOME/libgda) which a convenient wrapper
around multiple SQL providers. We have good support for PostgreSQL and
would like to keep so in the future. To test our code we use CI+Docker.
Docker provides SQL server functionality. Everything is fine but...

We have an API to run DDL operations. Everything works ok but once in a
while, we have a problem to create a new database if we run multiple
build processes communicating to the same SQL server. In our code, the
process to create a new database consist of the following steps:

1) Connect to "template1" database
2) Execute "CREATE DATABASE <some_name>"
3) Close connection
4) Open a connection to the new database.

I was trying to explore this problem using libpq to better understand
the origin of the problem. This is a test code:

void function_to_run_in_a_thread(void *data) {

/* The passed void *data is a pointer to an SQL string with a randomly
generated database name, e.g. CREATE DATABASE kajdygsj */
    const char *sql_create_db = (const char *)data;
    const char *cnc_string = "host='localhost' user='test'
password='test1' dbname='template1'";

    PGconn *cnc = NULL;
    PGresult *res = NULL;

    cnc = PQconnectdb(cnc_string);

    if (PQstatus(cnc) != CONNECTION_OK)
        abort();

    res = PQexec(cnc, sql_create_db);

    if (PQresultStatus(res) != PGRES_COMMAND_OK)
        abort();
     

    PQclear(res);
    PQfinish(cnc);
}

I run this function using only one thread and everything works great.
If I use two threads, the PQresultStatus(res) != PGRES_COMMAND_OK. I
was trying to add a delay time for PQresultStatus(res) !=
PGRES_COMMAND_OK and repeat the same command but didn't help much. If I
drop connection for PQresultStatus(res) != PGRES_COMMAND_OK and open it
again after some random delay, it works.

Can the server handle CREATE DATABASE requests in parallel?

Thanks,

-Pavlo



Reply | Threaded
Open this post in threaded view
|

Re: libpq CREATE DATABASE operation from multiple treads

Tom Lane-2
[hidden email] writes:
> I run this function using only one thread and everything works great.
> If I use two threads, the PQresultStatus(res) != PGRES_COMMAND_OK.

Are you trying to use the same PGconn from multiple threads?  That
will not work --- at least not without interlocks that libpq on
its own does not provide.

> Can the server handle CREATE DATABASE requests in parallel?

Sure.  But they have to be issued over different connections.
Any given connection can only do one thing at a time.

                        regards, tom lane


Reply | Threaded
Open this post in threaded view
|

Re: libpq CREATE DATABASE operation from multiple treads

p.sun.fun
Hi Tom.

Thanks for the quick response. In the function that I execute in each
thread, I create a new PGconn at the beginning. I expect that every
function caller (thread) should use a unique PGconn. I would not expect
that PGconn can be shared between threads. The documentation explicitly
says that.


On Thu, 2020-10-08 at 15:57 -0400, Tom Lane wrote:

> [hidden email] writes:
> > I run this function using only one thread and everything works
> > great.
> > If I use two threads, the PQresultStatus(res) != PGRES_COMMAND_OK.
>
> Are you trying to use the same PGconn from multiple threads?  That
> will not work --- at least not without interlocks that libpq on
> its own does not provide.
>
> > Can the server handle CREATE DATABASE requests in parallel?
>
> Sure.  But they have to be issued over different connections.
> Any given connection can only do one thing at a time.
>
> regards, tom lane



Reply | Threaded
Open this post in threaded view
|

Re: libpq CREATE DATABASE operation from multiple treads

Dmitry Igrishin
In reply to this post by p.sun.fun


On Thu, 8 Oct 2020, 22:46 , <[hidden email]> wrote:
Hi, PostgreSQL community.

I hope everyone is doing great and keep themselves safe and healthy.

I am not sure whether my question should belong here. Please advise if
this is the wrong place.

I am contributing to the libgda project (
https://gitlab.gnome.org/GNOME/libgda) which a convenient wrapper
around multiple SQL providers. We have good support for PostgreSQL and
would like to keep so in the future. To test our code we use CI+Docker.
Docker provides SQL server functionality. Everything is fine but...

We have an API to run DDL operations. Everything works ok but once in a
while, we have a problem to create a new database if we run multiple
build processes communicating to the same SQL server. In our code, the
process to create a new database consist of the following steps:

1) Connect to "template1" database
2) Execute "CREATE DATABASE <some_name>"
3) Close connection
4) Open a connection to the new database.

I was trying to explore this problem using libpq to better understand
the origin of the problem. This is a test code:

void function_to_run_in_a_thread(void *data) {

/* The passed void *data is a pointer to an SQL string with a randomly
generated database name, e.g. CREATE DATABASE kajdygsj */
    const char *sql_create_db = (const char *)data;
    const char *cnc_string = "host='localhost' user='test'
password='test1' dbname='template1'";

    PGconn *cnc = NULL;
    PGresult *res = NULL;

    cnc = PQconnectdb(cnc_string);

    if (PQstatus(cnc) != CONNECTION_OK)
        abort();       

    res = PQexec(cnc, sql_create_db);

    if (PQresultStatus(res) != PGRES_COMMAND_OK)
        abort();


    PQclear(res);
    PQfinish(cnc);
}

I run this function using only one thread and everything works great.
If I use two threads, the PQresultStatus(res) != PGRES_COMMAND_OK. I
was trying to add a delay time for PQresultStatus(res) !=
PGRES_COMMAND_OK and repeat the same command but didn't help much. If I
drop connection for PQresultStatus(res) != PGRES_COMMAND_OK and open it
again after some random delay, it works.
Okay. You may want to check PQresultStatus(). If it's PGRES_FATAL_ERROR please check the SQLSTATE and message.

Can the server handle CREATE DATABASE requests in parallel?

Thanks,

-Pavlo



Reply | Threaded
Open this post in threaded view
|

Re: libpq CREATE DATABASE operation from multiple treads

p.sun.fun
Indeed, this is a FATAL_ERROR. From two threads I got:

// Thread #1
FATAL_ERROR: ERROR:  source database "template1" is being accessed by
other users
DETAIL:  There is 1 other session using the database.

// Thread #2
FATAL_ERROR: ERROR:  source database "template1" is being accessed by
other users
DETAIL:  There is 1 other session using the database.



On Thu, 2020-10-08 at 23:15 +0300, Dmitry Igrishin wrote:

>
>
> On Thu, 8 Oct 2020, 22:46 , <[hidden email]> wrote:
> > Hi, PostgreSQL community.
> >
> > I hope everyone is doing great and keep themselves safe and
> > healthy.
> >
> > I am not sure whether my question should belong here. Please advise
> > if
> > this is the wrong place.
> >
> > I am contributing to the libgda project (
> > https://gitlab.gnome.org/GNOME/libgda) which a convenient wrapper
> > around multiple SQL providers. We have good support for PostgreSQL
> > and
> > would like to keep so in the future. To test our code we use
> > CI+Docker.
> > Docker provides SQL server functionality. Everything is fine but...
> >
> > We have an API to run DDL operations. Everything works ok but once
> > in a
> > while, we have a problem to create a new database if we run
> > multiple
> > build processes communicating to the same SQL server. In our code,
> > the
> > process to create a new database consist of the following steps:
> >
> > 1) Connect to "template1" database
> > 2) Execute "CREATE DATABASE <some_name>"
> > 3) Close connection
> > 4) Open a connection to the new database.
> >
> > I was trying to explore this problem using libpq to better
> > understand
> > the origin of the problem. This is a test code:
> >
> > void function_to_run_in_a_thread(void *data) {
> >
> > /* The passed void *data is a pointer to an SQL string with a
> > randomly
> > generated database name, e.g. CREATE DATABASE kajdygsj */
> >     const char *sql_create_db = (const char *)data;
> >     const char *cnc_string = "host='localhost' user='test'
> > password='test1' dbname='template1'";
> >
> >     PGconn *cnc = NULL;
> >     PGresult *res = NULL;
> >
> >     cnc = PQconnectdb(cnc_string);
> >
> >     if (PQstatus(cnc) != CONNECTION_OK)
> >         abort();        
> >
> >     res = PQexec(cnc, sql_create_db);
> >
> >     if (PQresultStatus(res) != PGRES_COMMAND_OK)
> >         abort();
> >
> >
> >     PQclear(res);
> >     PQfinish(cnc);
> > }
> >
> > I run this function using only one thread and everything works
> > great.
> > If I use two threads, the PQresultStatus(res) != PGRES_COMMAND_OK.
> > I
> > was trying to add a delay time for PQresultStatus(res) !=
> > PGRES_COMMAND_OK and repeat the same command but didn't help much.
> > If I
> > drop connection for PQresultStatus(res) != PGRES_COMMAND_OK and
> > open it
> > again after some random delay, it works.
>
> Okay. You may want to check PQresultStatus(). If it's
> PGRES_FATAL_ERROR please check the SQLSTATE and message.
> > Can the server handle CREATE DATABASE requests in parallel?
> >
> > Thanks,
> >
> > -Pavlo
> >
> >
> >



Reply | Threaded
Open this post in threaded view
|

Re: libpq CREATE DATABASE operation from multiple treads

Tom Lane-2
In reply to this post by p.sun.fun
[hidden email] writes:
> Thanks for the quick response. In the function that I execute in each
> thread, I create a new PGconn at the beginning. I expect that every
> function caller (thread) should use a unique PGconn. I would not expect
> that PGconn can be shared between threads. The documentation explicitly
> says that.

In that case you need to probe a little deeper.  What error message(s)
are you getting?  Is there anything relevant in the server's log?

                        regards, tom lane


lup
Reply | Threaded
Open this post in threaded view
|

Re: libpq CREATE DATABASE operation from multiple treads

lup
In reply to this post by p.sun.fun


> On Oct 8, 2020, at 2:30 PM, [hidden email] wrote:
>
> Indeed, this is a FATAL_ERROR. From two threads I got:
>
> // Thread #1
> FATAL_ERROR: ERROR:  source database "template1" is being accessed by
> other users
> DETAIL:  There is 1 other session using the database.
>
> // Thread #2
> FATAL_ERROR: ERROR:  source database "template1" is being accessed by
> other users
> DETAIL:  There is 1 other session using the database.
>
I would try using dbname=postgres.  If you get the same error, I would say your server is in single user mode?



Reply | Threaded
Open this post in threaded view
|

Re: libpq CREATE DATABASE operation from multiple treads

Tom Lane-2
In reply to this post by p.sun.fun
[hidden email] writes:
> Indeed, this is a FATAL_ERROR. From two threads I got:
> // Thread #1
> FATAL_ERROR: ERROR:  source database "template1" is being accessed by
> other users
> DETAIL:  There is 1 other session using the database.

You'd be better off to connect to some other database than template1;
not only just for this purpose, but in general.  If you are issuing
random commands in template1, there's a risk of modifying that database
unexpectedly and thereby changing the contents of databases created
in future.

                        regards, tom lane


Reply | Threaded
Open this post in threaded view
|

Re: libpq CREATE DATABASE operation from multiple treads

p.sun.fun
In reply to this post by lup
On Thu, 2020-10-08 at 14:35 -0600, Rob Sargent wrote:

> > On Oct 8, 2020, at 2:30 PM, [hidden email] wrote:
> >
> > Indeed, this is a FATAL_ERROR. From two threads I got:
> >
> > // Thread #1
> > FATAL_ERROR: ERROR:  source database "template1" is being accessed
> > by
> > other users
> > DETAIL:  There is 1 other session using the database.
> >
> > // Thread #2
> > FATAL_ERROR: ERROR:  source database "template1" is being accessed
> > by
> > other users
> > DETAIL:  There is 1 other session using the database.
> >
> I would try using dbname=postgres.  
aaaaand.... it works. Two databases have been created with return
status "COMMAND_OK".

> If you get the same error, I would say your server is in single user
> mode?
>



lup
Reply | Threaded
Open this post in threaded view
|

Re: libpq CREATE DATABASE operation from multiple treads

lup


On Oct 8, 2020, at 2:41 PM, [hidden email] wrote:

On Thu, 2020-10-08 at 14:35 -0600, Rob Sargent wrote:
On Oct 8, 2020, at 2:30 PM, [hidden email] wrote:

Indeed, this is a FATAL_ERROR. From two threads I got:

// Thread #1
FATAL_ERROR: ERROR:  source database "template1" is being accessed
by
other users
DETAIL:  There is 1 other session using the database.

// Thread #2
FATAL_ERROR: ERROR:  source database "template1" is being accessed
by
other users
DETAIL:  There is 1 other session using the database.

I would try using dbname=postgres.  
aaaaand.... it works. Two databases have been created with return
status "COMMAND_OK".

If you get the same error, I would say your server is in single user
mode?

And if you try it again using template1?

Reply | Threaded
Open this post in threaded view
|

Re: libpq CREATE DATABASE operation from multiple treads

p.sun.fun
On Thu, 2020-10-08 at 14:43 -0600, Rob Sargent wrote:

>
>
> > On Oct 8, 2020, at 2:41 PM, [hidden email] wrote:
> >
> > On Thu, 2020-10-08 at 14:35 -0600, Rob Sargent wrote:
> > > > On Oct 8, 2020, at 2:30 PM, [hidden email] wrote:
> > > >
> > > > Indeed, this is a FATAL_ERROR. From two threads I got:
> > > >
> > > > // Thread #1
> > > > FATAL_ERROR: ERROR:  source database "template1" is being
> > > > accessed
> > > > by
> > > > other users
> > > > DETAIL:  There is 1 other session using the database.
> > > >
> > > > // Thread #2
> > > > FATAL_ERROR: ERROR:  source database "template1" is being
> > > > accessed
> > > > by
> > > > other users
> > > > DETAIL:  There is 1 other session using the database.
> > > >
> > > I would try using dbname=postgres.  
> > aaaaand.... it works. Two databases have been created with return
> > status "COMMAND_OK".
> >
> > > If you get the same error, I would say your server is in single
> > > user
> > > mode?
>
> And if you try it again using template1?
>

Nope, the same errors:
// Thread #1
FATAL_ERROR: ERROR:  source database "template1" is being accessed by
other users
DETAIL:  There is 1 other session using the database.

// Thread #2
FATAL_ERROR: ERROR:  source database "template1" is being accessed by
other users
DETAIL:  There is 1 other session using the database.



lup
Reply | Threaded
Open this post in threaded view
|

Re: libpq CREATE DATABASE operation from multiple treads

lup


> On Oct 8, 2020, at 2:46 PM, [hidden email] wrote:
>
> On Thu, 2020-10-08 at 14:43 -0600, Rob Sargent wrote:
>>
>>
>>> On Oct 8, 2020, at 2:41 PM, [hidden email] wrote:
>>>
>>> On Thu, 2020-10-08 at 14:35 -0600, Rob Sargent wrote:
>>>>> On Oct 8, 2020, at 2:30 PM, [hidden email] wrote:
>>>>>
>>>>> Indeed, this is a FATAL_ERROR. From two threads I got:
>>>>>
>>>>> // Thread #1
>>>>> FATAL_ERROR: ERROR:  source database "template1" is being
>>>>> accessed
>>>>> by
>>>>> other users
>>>>> DETAIL:  There is 1 other session using the database.
>>>>>
>>>>> // Thread #2
>>>>> FATAL_ERROR: ERROR:  source database "template1" is being
>>>>> accessed
>>>>> by
>>>>> other users
>>>>> DETAIL:  There is 1 other session using the database.
>>>>>
>>>> I would try using dbname=postgres.  
>>> aaaaand.... it works. Two databases have been created with return
>>> status "COMMAND_OK".
>>>
>>>> If you get the same error, I would say your server is in single
>>>> user
>>>> mode?
>>
>> And if you try it again using template1?
>>
>
> Nope, the same errors:
> // Thread #1
> FATAL_ERROR: ERROR:  source database "template1" is being accessed by
> other users
> DETAIL:  There is 1 other session using the database.
>
> // Thread #2
> FATAL_ERROR: ERROR:  source database "template1" is being accessed by
> other users
> DETAIL:  There is 1 other session using the database.
>
>
>
OK, well that’s a special db.  Didn’t know it was that special, though!



Reply | Threaded
Open this post in threaded view
|

Re: libpq CREATE DATABASE operation from multiple treads

p.sun.fun
In reply to this post by Tom Lane-2
On Thu, 2020-10-08 at 16:36 -0400, Tom Lane wrote:
> [hidden email] writes:
> > Indeed, this is a FATAL_ERROR. From two threads I got:
> > // Thread #1
> > FATAL_ERROR: ERROR:  source database "template1" is being accessed
> > by
> > other users
> > DETAIL:  There is 1 other session using the database.
>
> You'd be better off to connect to some other database than template1;

What would be the best strategy to create a database from user
standpoint of view? If we have an API to create a database on a server.
The user doesn't know about database(s) yet. The possible option would
be: to check the result status and if is not COMMAND_OK, probably
connection should be dropped and established again.  

May be not so elegant but at least safe and stable.

> not only just for this purpose, but in general.  If you are issuing
> random commands in template1, there's a risk of modifying that
> database
> unexpectedly and thereby changing the contents of databases created
> in future.
>
> regards, tom lane



Reply | Threaded
Open this post in threaded view
|

Re: libpq CREATE DATABASE operation from multiple treads

Tom Lane-2
In reply to this post by lup
Rob Sargent <[hidden email]> writes:
> OK, well that’s a special db.  Didn’t know it was that special, though!

It's not that special.  The issue here is that each session is connecting
to template1 and then trying to clone template1.  You can't clone an
active database, because you might not get a consistent copy.  CREATE
DATABASE knows that its own session isn't concurrently making any
changes, so it allows copying the current database --- but it can't
know what some other session is doing, so if it sees some other session
is also connected to the source database, it spits up.

As I already said, routinely connecting to template1 is pretty bad
practice to start with, so the preferred answer is "don't do that".

                        regards, tom lane


Reply | Threaded
Open this post in threaded view
|

Re: libpq CREATE DATABASE operation from multiple treads

Tom Lane-2
In reply to this post by p.sun.fun
[hidden email] writes:
> On Thu, 2020-10-08 at 16:36 -0400, Tom Lane wrote:
>> You'd be better off to connect to some other database than template1;

> What would be the best strategy to create a database from user
> standpoint of view?

Connect to the postgres database.  That's what it's there for.

                        regards, tom lane


Reply | Threaded
Open this post in threaded view
|

Re: libpq CREATE DATABASE operation from multiple treads

p.sun.fun
In reply to this post by Tom Lane-2
On Thu, 2020-10-08 at 16:56 -0400, Tom Lane wrote:

> Rob Sargent <[hidden email]> writes:
> > OK, well that’s a special db.  Didn’t know it was that special,
> > though!
>
> It's not that special.  The issue here is that each session is
> connecting
> to template1 and then trying to clone template1.  You can't clone an
> active database, because you might not get a consistent copy.  CREATE
> DATABASE knows that its own session isn't concurrently making any
> changes, so it allows copying the current database --- but it can't
> know what some other session is doing, so if it sees some other
> session
> is also connected to the source database, it spits up.
>
> As I already said, routinely connecting to template1 is pretty bad
> practice to start with, so the preferred answer is "don't do that".
>
> regards, tom lane

Thank you, guys. I will switch to the "postgres" database as a default
one. IMHO, it is worth adding to the documentation into the CREATE
DATABASE section. I am glad that PostgreSQL has a strong community that
stays behind the product.



Reply | Threaded
Open this post in threaded view
|

Re: libpq CREATE DATABASE operation from multiple treads

Guillaume Lelarge-3
Le ven. 9 oct. 2020 à 05:33, <[hidden email]> a écrit :
On Thu, 2020-10-08 at 16:56 -0400, Tom Lane wrote:
> Rob Sargent <[hidden email]> writes:
> > OK, well that’s a special db.  Didn’t know it was that special,
> > though!
>
> It's not that special.  The issue here is that each session is
> connecting
> to template1 and then trying to clone template1.  You can't clone an
> active database, because you might not get a consistent copy.  CREATE
> DATABASE knows that its own session isn't concurrently making any
> changes, so it allows copying the current database --- but it can't
> know what some other session is doing, so if it sees some other
> session
> is also connected to the source database, it spits up.
>
> As I already said, routinely connecting to template1 is pretty bad
> practice to start with, so the preferred answer is "don't do that".
>
>                       regards, tom lane

Thank you, guys. I will switch to the "postgres" database as a default
one. IMHO, it is worth adding to the documentation into the CREATE
DATABASE section. I am glad that PostgreSQL has a strong community that
stays behind the product.

It's already in the documentation:

"Although it is possible to copy a database other than template1 by specifying its name as the template, this is not (yet) intended as a general-purpose “COPY DATABASE” facility. The principal limitation is that no other sessions can be connected to the template database while it is being copied. CREATE DATABASE will fail if any other connection exists when it starts; otherwise, new connections to the template database are locked out until CREATE DATABASE completes. See Section 22.3 for more information."

Reply | Threaded
Open this post in threaded view
|

Re: libpq CREATE DATABASE operation from multiple treads

p.sun.fun
On Fri, 2020-10-09 at 05:44 +0200, Guillaume Lelarge wrote:

> Le ven. 9 oct. 2020 à 05:33, <[hidden email]> a écrit :
> > On Thu, 2020-10-08 at 16:56 -0400, Tom Lane wrote:
> > > Rob Sargent <[hidden email]> writes:
> > > > OK, well that’s a special db.  Didn’t know it was that special,
> > > > though!
> > >
> > > It's not that special.  The issue here is that each session is
> > > connecting
> > > to template1 and then trying to clone template1.  You can't clone
> > an
> > > active database, because you might not get a consistent copy.
> > CREATE
> > > DATABASE knows that its own session isn't concurrently making any
> > > changes, so it allows copying the current database --- but it
> > can't
> > > know what some other session is doing, so if it sees some other
> > > session
> > > is also connected to the source database, it spits up.
> > >
> > > As I already said, routinely connecting to template1 is pretty
> > bad
> > > practice to start with, so the preferred answer is "don't do
> > that".
> > >
> > >                       regards, tom lane
> >
> > Thank you, guys. I will switch to the "postgres" database as a
> > default
> > one. IMHO, it is worth adding to the documentation into the CREATE
> > DATABASE section. I am glad that PostgreSQL has a strong community
> > that
> > stays behind the product.
>
> It's already in the documentation:
>
> "Although it is possible to copy a database other than template1 by
> specifying its name as the template, this is not (yet) intended as a
> general-purpose “COPY DATABASE” facility. The principal limitation is
> that no other sessions can be connected to the template database
> while it is being copied. CREATE DATABASE will fail if any other
> connection exists when it starts; otherwise, new connections to the
> template database are locked out until CREATE DATABASE completes.
> See Section 22.3 for more information."
>
> See https://www.postgresql.org/docs/13/sql-createdatabase.html.

Yep, you are right. Probably did't read carefully. Thanks for pointing
out.