Обсуждение: [psycopg] Changing set_session implementation

Поиск
Список
Период
Сортировка

[psycopg] Changing set_session implementation

От
Daniele Varrazzo
Дата:
Hello,

some time ago I've found a problem in production with psycopg's
set_session(): the current implementation changes the state of the
connection using "set default_transaction_read_only" and similar
session parameter. Unfortunately they don't play well with
transaction-level connection pools, so other application may receive
misconfigured connection.

I've changed the set_session/set_isolation_level/autocommit connection
methods and properties to use better the BEGIN parameters in order to
set the session characteristics without executing extra queries (as it
was the case before 2.4 or something). The new implementation doesn't
change session parameters, unless in autocommit (where no BEGIN is
issued, so there is no other way to honour transaction parameters).
So:

    conn.set_session(readonly=True)

will result in transactions starting with 'BEGIN READ ONLY'; after
commit the session will keep the server defaults. Conversely:

    conn.set_session(readonly=True, autocommit=True)

will behave as before and result in the execution of 'SET
default_transaction_read_only TO on', which will not make you win any
new friend if you use it with pgbouncer...

Other changes connected to this refactoring are:

- conn.set_isolation_level() will throw an exception if executed
during a transaction; previously it would have executed a quiet
ROLLBACK
- conn.isolation_level doesn't read the value from the server anymore
but will return a value stored in the Python connection object by the
user. The default value is the new ISOLATION_LEVEL_DEFAULT, meaning
"begin a transaction, but use whatever isolation level the server is
configured for".

I hope the new behaviour will make the adapter more predictable at no
performance price. A diff of the code and docs is available
https://github.com/psycopg/psycopg2/compare/no-set-default-session and
will be released with psycopg 2.7 (which should be imminent now, but
I'm happy to get feedback about this change).

Cheers,

-- Daniele


Re: [psycopg] Changing set_session implementation

От
Christophe Pettus
Дата:
Great work!

> On Feb 4, 2017, at 08:33, Daniele Varrazzo <daniele.varrazzo@gmail.com> wrote:
> - conn.isolation_level doesn't read the value from the server anymore
> but will return a value stored in the Python connection object by the
> user. The default value is the new ISOLATION_LEVEL_DEFAULT, meaning
> "begin a transaction, but use whatever isolation level the server is
> configured for".

Just to clarify, is this also now the default for starting a new transaction (i.e., it won't send a SET ISOLATION LEVEL
acrossby default)? 

--
-- Christophe Pettus
   xof@thebuild.com



Re: [psycopg] Changing set_session implementation

От
Daniele Varrazzo
Дата:
On Wed, Feb 8, 2017 at 10:47 AM, Christophe Pettus <xof@thebuild.com> wrote:

>> On Feb 4, 2017, at 08:33, Daniele Varrazzo <daniele.varrazzo@gmail.com> wrote:
>> - conn.isolation_level doesn't read the value from the server anymore
>> but will return a value stored in the Python connection object by the
>> user. The default value is the new ISOLATION_LEVEL_DEFAULT, meaning
>> "begin a transaction, but use whatever isolation level the server is
>> configured for".
>
> Just to clarify, is this also now the default for starting a new transaction (i.e., it won't send a SET ISOLATION
LEVELacross by default)? 

Correct: by default psycopg would just run BEGIN to start a new
transaction, leaving the choice of the isolation level to the database
config file, the connection setting etc. If an isolation level
different from DEFAULT is selected (or other session characteristics,
such as READ ONLY) then it will use "BEGIN ISOLATION  LEVEL ..." to
start further transactions.

-- Daniele


Re: [psycopg] Changing set_session implementation

От
Christophe Pettus
Дата:
> On Feb 8, 2017, at 05:35, Daniele Varrazzo <daniele.varrazzo@gmail.com> wrote:
> Correct: by default psycopg would just run BEGIN to start a new
> transaction, leaving the choice of the isolation level to the database
> config file, the connection setting etc. If an isolation level
> different from DEFAULT is selected (or other session characteristics,
> such as READ ONLY) then it will use "BEGIN ISOLATION  LEVEL ..." to
> start further transactions.

Exactly the behavior I was hoping for!  Anything that reduces the overheat chatter at session establishment is a plus
tome. 

Thanks!

--
-- Christophe Pettus
   xof@thebuild.com



Re: [psycopg] Changing set_session implementation

От
Karsten Hilbert
Дата:
On Sat, Feb 04, 2017 at 04:33:13PM +0000, Daniele Varrazzo wrote:

> I've changed the set_session/set_isolation_level/autocommit connection
> methods and properties to use better the BEGIN parameters
...
> Conversely:
>
>     conn.set_session(readonly=True, autocommit=True)

Does this mean that

    conn.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT)

is now a no-op as observed by the following log snippet or
just diverted to set_session(autocommit=True) ?


code:

    _log.debug('conn state now: %s', gmPG2.capture_conn_state(self.conn))
    _log.debug('setting isolation level to autocommit - should be redundant ?')
    self.conn.set_isolation_level(0)
    _log.debug('conn state now: %s', gmPG2.capture_conn_state(self.conn))

log:

    2017-07-27 16:03:01  DEBUG     gm.bootstrapper  MainThread (-1219281664)
(./bootstrap_gm_db_system.py::__create_db()#810): conn state now: <connection object at 0xb64eca04; dsn:
'dbname=gnumed_v21port=5432 user=postgres sslmode=prefer fallback_application_name=GNUmed client_encoding=utf8
application_name=postgres@template.db',closed: 0> 
                       type: <class 'psycopg2.extras.DictConnection'>
                   identity: 3058616836
                backend PID: 2864
           protocol version: 3
                   encoding: UTF8
    isolation level (psyco): 3 (ISOLATION_LEVEL_SERIALIZABLE)
                   readonly: False
                 autocommit: True
                     closed: 0
          connection status: 1 (STATUS_READY)
         transaction status: 0 (TRANSACTION_STATUS_IDLE)
                 deferrable: None
                      async: 0
         executing async op: False
    2017-07-27 16:03:01  DEBUG     gm.bootstrapper  MainThread (-1219281664)
(./bootstrap_gm_db_system.py::__create_db()#811): setting isolation level to autocommit - should be redundant ? 
    2017-07-27 16:03:01  DEBUG     gm.bootstrapper  MainThread (-1219281664)
(./bootstrap_gm_db_system.py::__create_db()#813): conn state now: <connection object at 0xb64eca04; dsn:
'dbname=gnumed_v21port=5432 user=postgres sslmode=prefer fallback_application_name=GNUmed client_encoding=utf8
application_name=postgres@template.db',closed: 0> 
                       type: <class 'psycopg2.extras.DictConnection'>
                   identity: 3058616836
                backend PID: 2864
           protocol version: 3
                   encoding: UTF8
    isolation level (psyco): 3 (ISOLATION_LEVEL_SERIALIZABLE)
                   readonly: False
                 autocommit: True
                     closed: 0
          connection status: 1 (STATUS_READY)
         transaction status: 0 (TRANSACTION_STATUS_IDLE)
                 deferrable: None
                      async: 0
         executing async op: False


Note that it doesn't actually change the isolation level
(which makes sense because autocommit mode is not an
isolation level). Should conn.isolation_level show
ISOLATION_LEVEL_DEFAULT in this case (meaning "use what the
server/db is configured for") ?  It doesn't seem to make a
roundtrip to the server to _get_ the default because the
database in question defaults to READ_COMMITED. The
SERIALIZABLE results from an earlier call to
.set_isolation_mode() in my code.

(Also note that the connection had already been set to
 autocommit by "set_session(autocommit=True)" before.)


Thanks for the clarification,
Karsten
--
GPG key ID E4071346 @ eu.pool.sks-keyservers.net
E167 67FD A291 2BEA 73BD  4537 78B9 A9F9 E407 1346


Re: [psycopg] Changing set_session implementation

От
Karsten Hilbert
Дата:
Hopefully I just hit summer vacation -- or else do I need to
provide more information to get an answer on the below ?

Thanks,
Karsten

On Thu, Jul 27, 2017 at 04:17:33PM +0200, Karsten Hilbert wrote:

> On Sat, Feb 04, 2017 at 04:33:13PM +0000, Daniele Varrazzo wrote:
>
> > I've changed the set_session/set_isolation_level/autocommit connection
> > methods and properties to use better the BEGIN parameters
> ...
> > Conversely:
> >
> >     conn.set_session(readonly=True, autocommit=True)
>
> Does this mean that
>
>     conn.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT)
>
> is now a no-op as observed by the following log snippet or
> just diverted to set_session(autocommit=True) ?
>
>
> code:
>
>     _log.debug('conn state now: %s', gmPG2.capture_conn_state(self.conn))
>     _log.debug('setting isolation level to autocommit - should be redundant ?')
>     self.conn.set_isolation_level(0)
>     _log.debug('conn state now: %s', gmPG2.capture_conn_state(self.conn))
>
> log:
>
>     2017-07-27 16:03:01  DEBUG     gm.bootstrapper  MainThread (-1219281664)
(./bootstrap_gm_db_system.py::__create_db()#810): conn state now: <connection object at 0xb64eca04; dsn:
'dbname=gnumed_v21port=5432 user=postgres sslmode=prefer fallback_application_name=GNUmed client_encoding=utf8
application_name=postgres@template.db',closed: 0> 
>                        type: <class 'psycopg2.extras.DictConnection'>
>                    identity: 3058616836
>                 backend PID: 2864
>            protocol version: 3
>                    encoding: UTF8
>     isolation level (psyco): 3 (ISOLATION_LEVEL_SERIALIZABLE)
>                    readonly: False
>                  autocommit: True
>                      closed: 0
>           connection status: 1 (STATUS_READY)
>          transaction status: 0 (TRANSACTION_STATUS_IDLE)
>                  deferrable: None
>                       async: 0
>          executing async op: False
>     2017-07-27 16:03:01  DEBUG     gm.bootstrapper  MainThread (-1219281664)
(./bootstrap_gm_db_system.py::__create_db()#811): setting isolation level to autocommit - should be redundant ? 
>     2017-07-27 16:03:01  DEBUG     gm.bootstrapper  MainThread (-1219281664)
(./bootstrap_gm_db_system.py::__create_db()#813): conn state now: <connection object at 0xb64eca04; dsn:
'dbname=gnumed_v21port=5432 user=postgres sslmode=prefer fallback_application_name=GNUmed client_encoding=utf8
application_name=postgres@template.db',closed: 0> 
>                        type: <class 'psycopg2.extras.DictConnection'>
>                    identity: 3058616836
>                 backend PID: 2864
>            protocol version: 3
>                    encoding: UTF8
>     isolation level (psyco): 3 (ISOLATION_LEVEL_SERIALIZABLE)
>                    readonly: False
>                  autocommit: True
>                      closed: 0
>           connection status: 1 (STATUS_READY)
>          transaction status: 0 (TRANSACTION_STATUS_IDLE)
>                  deferrable: None
>                       async: 0
>          executing async op: False
>
>
> Note that it doesn't actually change the isolation level
> (which makes sense because autocommit mode is not an
> isolation level). Should conn.isolation_level show
> ISOLATION_LEVEL_DEFAULT in this case (meaning "use what the
> server/db is configured for") ?  It doesn't seem to make a
> roundtrip to the server to _get_ the default because the
> database in question defaults to READ_COMMITED. The
> SERIALIZABLE results from an earlier call to
> .set_isolation_mode() in my code.
>
> (Also note that the connection had already been set to
>  autocommit by "set_session(autocommit=True)" before.)
>
>
> Thanks for the clarification,
> Karsten
> --
> GPG key ID E4071346 @ eu.pool.sks-keyservers.net
> E167 67FD A291 2BEA 73BD  4537 78B9 A9F9 E407 1346
>
>
> --
> Sent via psycopg mailing list (psycopg@postgresql.org)
> To make changes to your subscription:
> http://www.postgresql.org/mailpref/psycopg

--
GPG key ID E4071346 @ eu.pool.sks-keyservers.net
E167 67FD A291 2BEA 73BD  4537 78B9 A9F9 E407 1346


Re: [psycopg] Changing set_session implementation

От
Daniele Varrazzo
Дата:
Hi Karsten, sorry for the late reply,

no, `conn.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT)` is not a no-op:

    In [1]: import psycopg2

    In [2]: psycopg2.__version__
    Out[2]: '2.7.3 (dt dec pq3 ext lo64)'

    In [3]: cnn = psycopg2.connect('')

    In [4]: cnn.autocommit
    Out[4]: False

    In [5]: cnn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT)

    In [6]: cnn.autocommit
    Out[6]: True

but if your connection was already autocommit then sure it will not do anything.

If you want to be absolutely sure about what the adapter does I
suggest you to enable statements log on the server and check what
statements are produced by psycopg. If you find any behaviour
inconsistent with what documented please let us know.

Hope this helps

-- Daniele


On Mon, Jul 31, 2017 at 11:51 AM, Karsten Hilbert
<Karsten.Hilbert@gmx.net> wrote:
> Hopefully I just hit summer vacation -- or else do I need to
> provide more information to get an answer on the below ?
>
> Thanks,
> Karsten
>
> On Thu, Jul 27, 2017 at 04:17:33PM +0200, Karsten Hilbert wrote:
>
>> On Sat, Feb 04, 2017 at 04:33:13PM +0000, Daniele Varrazzo wrote:
>>
>> > I've changed the set_session/set_isolation_level/autocommit connection
>> > methods and properties to use better the BEGIN parameters
>> ...
>> > Conversely:
>> >
>> >     conn.set_session(readonly=True, autocommit=True)
>>
>> Does this mean that
>>
>>       conn.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT)
>>
>> is now a no-op as observed by the following log snippet or
>> just diverted to set_session(autocommit=True) ?
>>
>>
>> code:
>>
>>       _log.debug('conn state now: %s', gmPG2.capture_conn_state(self.conn))
>>       _log.debug('setting isolation level to autocommit - should be redundant ?')
>>       self.conn.set_isolation_level(0)
>>       _log.debug('conn state now: %s', gmPG2.capture_conn_state(self.conn))
>>
>> log:
>>
>>       2017-07-27 16:03:01  DEBUG     gm.bootstrapper  MainThread (-1219281664)
(./bootstrap_gm_db_system.py::__create_db()#810): conn state now: <connection object at 0xb64eca04; dsn:
'dbname=gnumed_v21port=5432 user=postgres sslmode=prefer fallback_application_name=GNUmed client_encoding=utf8
application_name=postgres@template.db',closed: 0> 
>>                          type: <class 'psycopg2.extras.DictConnection'>
>>                      identity: 3058616836
>>                   backend PID: 2864
>>              protocol version: 3
>>                      encoding: UTF8
>>       isolation level (psyco): 3 (ISOLATION_LEVEL_SERIALIZABLE)
>>                      readonly: False
>>                    autocommit: True
>>                        closed: 0
>>             connection status: 1 (STATUS_READY)
>>            transaction status: 0 (TRANSACTION_STATUS_IDLE)
>>                    deferrable: None
>>                         async: 0
>>            executing async op: False
>>       2017-07-27 16:03:01  DEBUG     gm.bootstrapper  MainThread (-1219281664)
(./bootstrap_gm_db_system.py::__create_db()#811): setting isolation level to autocommit - should be redundant ? 
>>       2017-07-27 16:03:01  DEBUG     gm.bootstrapper  MainThread (-1219281664)
(./bootstrap_gm_db_system.py::__create_db()#813): conn state now: <connection object at 0xb64eca04; dsn:
'dbname=gnumed_v21port=5432 user=postgres sslmode=prefer fallback_application_name=GNUmed client_encoding=utf8
application_name=postgres@template.db',closed: 0> 
>>                          type: <class 'psycopg2.extras.DictConnection'>
>>                      identity: 3058616836
>>                   backend PID: 2864
>>              protocol version: 3
>>                      encoding: UTF8
>>       isolation level (psyco): 3 (ISOLATION_LEVEL_SERIALIZABLE)
>>                      readonly: False
>>                    autocommit: True
>>                        closed: 0
>>             connection status: 1 (STATUS_READY)
>>            transaction status: 0 (TRANSACTION_STATUS_IDLE)
>>                    deferrable: None
>>                         async: 0
>>            executing async op: False
>>
>>
>> Note that it doesn't actually change the isolation level
>> (which makes sense because autocommit mode is not an
>> isolation level). Should conn.isolation_level show
>> ISOLATION_LEVEL_DEFAULT in this case (meaning "use what the
>> server/db is configured for") ?  It doesn't seem to make a
>> roundtrip to the server to _get_ the default because the
>> database in question defaults to READ_COMMITED. The
>> SERIALIZABLE results from an earlier call to
>> .set_isolation_mode() in my code.
>>
>> (Also note that the connection had already been set to
>>  autocommit by "set_session(autocommit=True)" before.)
>>
>>
>> Thanks for the clarification,
>> Karsten
>> --
>> GPG key ID E4071346 @ eu.pool.sks-keyservers.net
>> E167 67FD A291 2BEA 73BD  4537 78B9 A9F9 E407 1346
>>
>>
>> --
>> Sent via psycopg mailing list (psycopg@postgresql.org)
>> To make changes to your subscription:
>> http://www.postgresql.org/mailpref/psycopg
>
> --
> GPG key ID E4071346 @ eu.pool.sks-keyservers.net
> E167 67FD A291 2BEA 73BD  4537 78B9 A9F9 E407 1346
>
>
> --
> Sent via psycopg mailing list (psycopg@postgresql.org)
> To make changes to your subscription:
> http://www.postgresql.org/mailpref/psycopg


Re: [psycopg] Changing set_session implementation

От
Karsten Hilbert
Дата:
On Wed, Aug 02, 2017 at 03:10:15PM +0100, Daniele Varrazzo wrote:

> Hi Karsten, sorry for the late reply,

No problem, thanks.

> no, `conn.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT)` is not a no-op:
>
>     In [1]: import psycopg2
>
>     In [2]: psycopg2.__version__
>     Out[2]: '2.7.3 (dt dec pq3 ext lo64)'
>
>     In [3]: cnn = psycopg2.connect('')
>
>     In [4]: cnn.autocommit
>     Out[4]: False
>
>     In [5]: cnn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT)
>
>     In [6]: cnn.autocommit
>     Out[6]: True

I see. I thought as much. Put another way,

    cnn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT)

and

    cnn.set_session(autocommit = True)

do the same thing.

Thanks,
Karsten
--
GPG key ID E4071346 @ eu.pool.sks-keyservers.net
E167 67FD A291 2BEA 73BD  4537 78B9 A9F9 E407 1346


Re: [psycopg] Changing set_session implementation

От
Karsten Hilbert
Дата:
On Wed, Aug 02, 2017 at 03:10:15PM +0100, Daniele Varrazzo wrote:

> If you want to be absolutely sure about what the adapter does I
> suggest you to enable statements log on the server and check what
> statements are produced by psycopg. If you find any behaviour
> inconsistent with what documented please let us know.

One thing I noticed is that it seems pycopg2 does not send a
"set default_transaction_read_only to off" when

- a database had been created and ALTERed to permanently
  be "default_transaction_read_only to ON"

- a transaction had been opened on a connection with

        BEGIN ISOLATION LEVEL SERIALIZABLE READ WRITE

  in order to run various comments (such as setting
  default client timezone)

- that transaction had been committed

- the connection is switched to autocommit=true
    (for running DROP DATABASE et al)

        2017-08-02 19:18:13 GMT LOG:  00000: Verbindung empfangen: Host=[local]
        2017-08-02 19:18:13 GMT ORT:  BackendInitialize, postmaster.c:4135
        2017-08-02 19:18:13 GMT LOG:  00000: Verbindung autorisiert: Benutzer=postgres Datenbank=gnumed_v20
        2017-08-02 19:18:13 GMT ORT:  PerformAuthentication, postinit.c:272
        2017-08-02 19:18:13 GMT LOG:  00000: statement: BEGIN ISOLATION LEVEL SERIALIZABLE READ WRITE
        2017-08-02 19:18:13 GMT LOCATION:  exec_simple_query, postgres.c:935
        2017-08-02 19:18:13 GMT LOG:  00000: statement: set timezone to 'Europe/Madrid'
        2017-08-02 19:18:13 GMT LOCATION:  exec_simple_query, postgres.c:935
        2017-08-02 19:18:13 GMT LOG:  00000: statement: COMMIT
        2017-08-02 19:18:13 GMT LOCATION:  exec_simple_query, postgres.c:935
        2017-08-02 19:18:13 GMT LOG:  00000: statement: BEGIN ISOLATION LEVEL SERIALIZABLE READ WRITE
        2017-08-02 19:18:13 GMT LOCATION:  exec_simple_query, postgres.c:935
        2017-08-02 19:18:13 GMT LOG:  00000: statement: set lc_messages to 'C'
        2017-08-02 19:18:13 GMT LOCATION:  exec_simple_query, postgres.c:935
        2017-08-02 19:18:13 GMT LOG:  00000: statement: select md5(gm.concat_table_structure(20::integer)) as md5
        2017-08-02 19:18:13 GMT LOCATION:  exec_simple_query, postgres.c:935
        2017-08-02 19:18:14 GMT LOG:  00000: duration: 1156.407 ms
        2017-08-02 19:18:14 GMT LOCATION:  exec_simple_query, postgres.c:1164
        2017-08-02 19:18:14 GMT LOG:  00000: statement: SELECT datname FROM pg_database WHERE datname='gnumed_v21'
        2017-08-02 19:18:14 GMT LOCATION:  exec_simple_query, postgres.c:935
        2017-08-02 19:18:14 GMT LOG:  00000: statement: COMMIT
        2017-08-02 19:18:14 GMT LOCATION:  exec_simple_query, postgres.c:935
        2017-08-02 19:18:14 GMT LOG:  00000: statement: DROP DATABASE "gnumed_v21"
        2017-08-02 19:18:14 GMT LOCATION:  exec_simple_query, postgres.c:935
        2017-08-02 19:18:14 GMT ERROR:  25006: cannot execute DROP DATABASE in a read-only transaction
        2017-08-02 19:18:14 GMT LOCATION:  PreventCommandIfReadOnly, utility.c:236
        2017-08-02 19:18:14 GMT STATEMENT:  DROP DATABASE "gnumed_v21"
        2017-08-02 19:18:14 GMT LOG:  00000: statement: SET default_transaction_isolation TO DEFAULT
        2017-08-02 19:18:14 GMT LOCATION:  exec_simple_query, postgres.c:935
        2017-08-02 19:18:14 GMT LOG:  00000: statement: SET default_transaction_read_only TO DEFAULT
        2017-08-02 19:18:14 GMT LOCATION:  exec_simple_query, postgres.c:935
        2017-08-02 19:18:14 GMT LOG:  00000: disconnection: session time: 0:00:01.183 user=postgres database=gnumed_v20
host=[local]
        2017-08-02 19:18:14 GMT LOCATION:  log_disconnections, postgres.c:4501

The only other indirect proof I have for this is: if I insert
a manual "set default_transaction_read_only to off" right
before the "drop database ..." (but after having been
switched to autocommit) it works as expected.

If the connection has NOT been used for any other
transactions (that is, if it is switched to autocommit=true
and then to readonly=off) it also works as expected -
psycopg2 sends a "set default_transaction_read_only to off".

Karsten
--
GPG key ID E4071346 @ eu.pool.sks-keyservers.net
E167 67FD A291 2BEA 73BD  4537 78B9 A9F9 E407 1346


Re: [psycopg] Changing set_session implementation

От
Karsten Hilbert
Дата:
Regarding the below problem with readwrite autocommit
connections in default-readonly databases I have whittled
down a test case to the following (attached):

    db = u'gnumed_v20'        # a database configured "alter database %s set default_transaction_read_only to on"
    user = 'gm-dbo'            # a user with CREATE DATABASE powers

    cmd_def_tx_ro = "SELECT upper(source), name, upper(setting) FROM pg_settings WHERE name =
'default_transaction_read_only'"
    cmd_create_db = "create database %s_copy template %s" % (db, db)

    import sys
    import psycopg2

    conn = psycopg2.connect(dbname = db, user = user)
    print 'conn:', conn
    print 'readonly:', conn.readonly
    print 'autocommit:', conn.autocommit
    conn.autocommit = False
    conn.readonly = False                            # <========== comment out for success
    conn.autocommit = True
    print 'autocommit now:', conn.autocommit
    conn.readonly = False
    print 'readonly now:', conn.readonly
    curs = conn.cursor()
    curs.execute(cmd_def_tx_ro)
    print 'should show DEFAULT_TRANSACTION_READ_ONLY set to ON'
    print curs.fetchall()
    curs.close()
    conn.commit()
    print 'the following CREATE DATABASE should fail'
    curs = conn.cursor()
    try:
        curs.execute(cmd_create_db)
    except psycopg2.InternalError as ex:
        print 'SQL failed:'
        print ex

    print 'shutting down'

    curs.close()
    conn.rollback()
    conn.close()

which shows on the console:

    ncq@hermes:~$ python x-test-default_ro.py
    conn: <connection object at 0xb674e6bc; dsn: 'dbname=gnumed_v20 user=gm-dbo', closed: 0>
    readonly: None
    autocommit: False
    autocommit now: True
    readonly now: False
    should show DEFAULT_TRANSACTION_READ_ONLY set to ON
    [('DATABASE', 'default_transaction_read_only', 'ON')]
    the following CREATE DATABASE should fail
    SQL failed:
    cannot execute CREATE DATABASE in a read-only transaction

    shutting down

The PostgreSQL log shows:

    2017-08-03 21:00:19 GMT LOG:  00000: Verbindung empfangen: Host=[local]
    2017-08-03 21:00:19 GMT ORT:  BackendInitialize, postmaster.c:4135
    2017-08-03 21:00:19 GMT LOG:  00000: Verbindung autorisiert: Benutzer=gm-dbo Datenbank=gnumed_v20
    2017-08-03 21:00:19 GMT ORT:  PerformAuthentication, postinit.c:272
    2017-08-03 21:00:19 GMT LOG:  00000: statement: SELECT upper(source), name, upper(setting) FROM pg_settings WHERE
name= 'default_transaction_read_only' 
    2017-08-03 21:00:19 GMT LOCATION:  exec_simple_query, postgres.c:935
    2017-08-03 21:00:19 GMT LOG:  00000: statement: create database gnumed_v20_copy template gnumed_v20
    2017-08-03 21:00:19 GMT LOCATION:  exec_simple_query, postgres.c:935
    2017-08-03 21:00:19 GMT ERROR:  25006: cannot execute CREATE DATABASE in a read-only transaction
    2017-08-03 21:00:19 GMT LOCATION:  PreventCommandIfReadOnly, utility.c:236
    2017-08-03 21:00:19 GMT STATEMENT:  create database gnumed_v20_copy template gnumed_v20
    2017-08-03 21:00:19 GMT LOG:  00000: disconnection: session time: 0:00:00.007 user=gm-dbo database=gnumed_v20
host=[local]
    2017-08-03 21:00:19 GMT LOCATION:  log_disconnections, postgres.c:4501

Commenting out the marked line makes it work and the log shows:

    2017-08-03 21:01:24 GMT LOG:  00000: Verbindung empfangen: Host=[local]
    2017-08-03 21:01:24 GMT ORT:  BackendInitialize, postmaster.c:4135
    2017-08-03 21:01:24 GMT LOG:  00000: Verbindung autorisiert: Benutzer=postgres Datenbank=postgres
    2017-08-03 21:01:24 GMT ORT:  PerformAuthentication, postinit.c:272
    2017-08-03 21:01:25 GMT LOG:  00000: Anweisung: drop database gnumed_v20_copy ;
    2017-08-03 21:01:25 GMT ORT:  exec_simple_query, postgres.c:935
    2017-08-03 21:01:26 GMT LOG:  00000: Verbindungsende: Sitzungszeit: 0:00:02.324 Benutzer=postgres
Datenbank=postgresHost=[local] 
    2017-08-03 21:01:26 GMT ORT:  log_disconnections, postgres.c:4501
    2017-08-03 21:01:33 GMT LOG:  00000: Verbindung empfangen: Host=[local]
    2017-08-03 21:01:33 GMT ORT:  BackendInitialize, postmaster.c:4135
    2017-08-03 21:01:33 GMT LOG:  00000: Verbindung autorisiert: Benutzer=gm-dbo Datenbank=gnumed_v20
    2017-08-03 21:01:33 GMT ORT:  PerformAuthentication, postinit.c:272
    2017-08-03 21:01:33 GMT LOG:  00000: statement: SET default_transaction_read_only TO 'off'
    2017-08-03 21:01:33 GMT LOCATION:  exec_simple_query, postgres.c:935
    2017-08-03 21:01:33 GMT LOG:  00000: statement: SELECT upper(source), name, upper(setting) FROM pg_settings WHERE
name= 'default_transaction_read_only' 
    2017-08-03 21:01:33 GMT LOCATION:  exec_simple_query, postgres.c:935
    2017-08-03 21:01:33 GMT LOG:  00000: statement: create database gnumed_v20_copy template gnumed_v20
    2017-08-03 21:01:33 GMT LOCATION:  exec_simple_query, postgres.c:935
    2017-08-03 21:01:37 GMT LOG:  00000: duration: 3447.360 ms
    2017-08-03 21:01:37 GMT LOCATION:  exec_simple_query, postgres.c:1164
    2017-08-03 21:01:37 GMT LOG:  00000: disconnection: session time: 0:00:03.454 user=gm-dbo database=gnumed_v20
host=[local]
    2017-08-03 21:01:37 GMT LOCATION:  log_disconnections, postgres.c:4501

So, under some circumstances psycopg2 does send the

    2017-08-03 21:01:33 GMT LOG:  00000: statement: SET default_transaction_read_only TO 'off'

while sometimes it does not, despite both times being in
autocommit=True and readonly=False when the "create database"
is sent.

Of course, the

    conn.autocommit = False
    conn.readonly = False                            # <========== comment out for success
    conn.autocommit = True
    conn.readonly = False

seems redundant but that is the relevant sequence of events
condensed from a larger body of code. Redundant as it may
seem it should still work, no ?

I am not sure I fully grok what is happening...

Thanks for insights,
Karsten


On Wed, Aug 02, 2017 at 09:38:21PM +0200, Karsten Hilbert wrote:

> On Wed, Aug 02, 2017 at 03:10:15PM +0100, Daniele Varrazzo wrote:
>
> > If you want to be absolutely sure about what the adapter does I
> > suggest you to enable statements log on the server and check what
> > statements are produced by psycopg. If you find any behaviour
> > inconsistent with what documented please let us know.
>
> One thing I noticed is that it seems pycopg2 does not send a
> "set default_transaction_read_only to off" when
>
> - a database had been created and ALTERed to permanently
>   be "default_transaction_read_only to ON"
>
> - a transaction had been opened on a connection with
>
>         BEGIN ISOLATION LEVEL SERIALIZABLE READ WRITE
>
>   in order to run various comments (such as setting
>   default client timezone)
>
> - that transaction had been committed
>
> - the connection is switched to autocommit=true
>     (for running DROP DATABASE et al)
>
>         2017-08-02 19:18:13 GMT LOG:  00000: Verbindung empfangen: Host=[local]
>         2017-08-02 19:18:13 GMT ORT:  BackendInitialize, postmaster.c:4135
>         2017-08-02 19:18:13 GMT LOG:  00000: Verbindung autorisiert: Benutzer=postgres Datenbank=gnumed_v20
>         2017-08-02 19:18:13 GMT ORT:  PerformAuthentication, postinit.c:272
>         2017-08-02 19:18:13 GMT LOG:  00000: statement: BEGIN ISOLATION LEVEL SERIALIZABLE READ WRITE
>         2017-08-02 19:18:13 GMT LOCATION:  exec_simple_query, postgres.c:935
>         2017-08-02 19:18:13 GMT LOG:  00000: statement: set timezone to 'Europe/Madrid'
>         2017-08-02 19:18:13 GMT LOCATION:  exec_simple_query, postgres.c:935
>         2017-08-02 19:18:13 GMT LOG:  00000: statement: COMMIT
>         2017-08-02 19:18:13 GMT LOCATION:  exec_simple_query, postgres.c:935
>         2017-08-02 19:18:13 GMT LOG:  00000: statement: BEGIN ISOLATION LEVEL SERIALIZABLE READ WRITE
>         2017-08-02 19:18:13 GMT LOCATION:  exec_simple_query, postgres.c:935
>         2017-08-02 19:18:13 GMT LOG:  00000: statement: set lc_messages to 'C'
>         2017-08-02 19:18:13 GMT LOCATION:  exec_simple_query, postgres.c:935
>         2017-08-02 19:18:13 GMT LOG:  00000: statement: select md5(gm.concat_table_structure(20::integer)) as md5
>         2017-08-02 19:18:13 GMT LOCATION:  exec_simple_query, postgres.c:935
>         2017-08-02 19:18:14 GMT LOG:  00000: duration: 1156.407 ms
>         2017-08-02 19:18:14 GMT LOCATION:  exec_simple_query, postgres.c:1164
>         2017-08-02 19:18:14 GMT LOG:  00000: statement: SELECT datname FROM pg_database WHERE datname='gnumed_v21'
>         2017-08-02 19:18:14 GMT LOCATION:  exec_simple_query, postgres.c:935
>         2017-08-02 19:18:14 GMT LOG:  00000: statement: COMMIT
>         2017-08-02 19:18:14 GMT LOCATION:  exec_simple_query, postgres.c:935
>         2017-08-02 19:18:14 GMT LOG:  00000: statement: DROP DATABASE "gnumed_v21"
>         2017-08-02 19:18:14 GMT LOCATION:  exec_simple_query, postgres.c:935
>         2017-08-02 19:18:14 GMT ERROR:  25006: cannot execute DROP DATABASE in a read-only transaction
>         2017-08-02 19:18:14 GMT LOCATION:  PreventCommandIfReadOnly, utility.c:236
>         2017-08-02 19:18:14 GMT STATEMENT:  DROP DATABASE "gnumed_v21"
>         2017-08-02 19:18:14 GMT LOG:  00000: statement: SET default_transaction_isolation TO DEFAULT
>         2017-08-02 19:18:14 GMT LOCATION:  exec_simple_query, postgres.c:935
>         2017-08-02 19:18:14 GMT LOG:  00000: statement: SET default_transaction_read_only TO DEFAULT
>         2017-08-02 19:18:14 GMT LOCATION:  exec_simple_query, postgres.c:935
>         2017-08-02 19:18:14 GMT LOG:  00000: disconnection: session time: 0:00:01.183 user=postgres
database=gnumed_v20host=[local] 
>         2017-08-02 19:18:14 GMT LOCATION:  log_disconnections, postgres.c:4501
>
> The only other indirect proof I have for this is: if I insert
> a manual "set default_transaction_read_only to off" right
> before the "drop database ..." (but after having been
> switched to autocommit) it works as expected.
>
> If the connection has NOT been used for any other
> transactions (that is, if it is switched to autocommit=true
> and then to readonly=off) it also works as expected -
> psycopg2 sends a "set default_transaction_read_only to off".
>
> Karsten
> --
> GPG key ID E4071346 @ eu.pool.sks-keyservers.net
> E167 67FD A291 2BEA 73BD  4537 78B9 A9F9 E407 1346
>
>
> --
> Sent via psycopg mailing list (psycopg@postgresql.org)
> To make changes to your subscription:
> http://www.postgresql.org/mailpref/psycopg

--
GPG key ID E4071346 @ eu.pool.sks-keyservers.net
E167 67FD A291 2BEA 73BD  4537 78B9 A9F9 E407 1346

Вложения

[psycopg] problem with readonly/autocommit, was: Changing set_sessionimplementation

От
Karsten Hilbert
Дата:
Hi,

has anyone been able to reproduce the problem described
below?  I'd be glad to provide more information if needed.

Thanks !

Karsten


On Thu, Aug 03, 2017 at 11:08:18PM +0200, Karsten Hilbert wrote:

> Regarding the below problem with readwrite autocommit
> connections in default-readonly databases I have whittled
> down a test case to the following (attached):
>
>     db = u'gnumed_v20'        # a database configured "alter database %s set default_transaction_read_only to on"
>     user = 'gm-dbo'            # a user with CREATE DATABASE powers
>
>     cmd_def_tx_ro = "SELECT upper(source), name, upper(setting) FROM pg_settings WHERE name =
'default_transaction_read_only'"
>     cmd_create_db = "create database %s_copy template %s" % (db, db)
>
>     import sys
>     import psycopg2
>
>     conn = psycopg2.connect(dbname = db, user = user)
>     print 'conn:', conn
>     print 'readonly:', conn.readonly
>     print 'autocommit:', conn.autocommit
>     conn.autocommit = False
>     conn.readonly = False                            # <========== comment out for success
>     conn.autocommit = True
>     print 'autocommit now:', conn.autocommit
>     conn.readonly = False
>     print 'readonly now:', conn.readonly
>     curs = conn.cursor()
>     curs.execute(cmd_def_tx_ro)
>     print 'should show DEFAULT_TRANSACTION_READ_ONLY set to ON'
>     print curs.fetchall()
>     curs.close()
>     conn.commit()
>     print 'the following CREATE DATABASE should fail'
>     curs = conn.cursor()
>     try:
>         curs.execute(cmd_create_db)
>     except psycopg2.InternalError as ex:
>         print 'SQL failed:'
>         print ex
>
>     print 'shutting down'
>
>     curs.close()
>     conn.rollback()
>     conn.close()
>
> which shows on the console:
>
>     ncq@hermes:~$ python x-test-default_ro.py
>     conn: <connection object at 0xb674e6bc; dsn: 'dbname=gnumed_v20 user=gm-dbo', closed: 0>
>     readonly: None
>     autocommit: False
>     autocommit now: True
>     readonly now: False
>     should show DEFAULT_TRANSACTION_READ_ONLY set to ON
>     [('DATABASE', 'default_transaction_read_only', 'ON')]
>     the following CREATE DATABASE should fail
>     SQL failed:
>     cannot execute CREATE DATABASE in a read-only transaction
>
>     shutting down
>
> The PostgreSQL log shows:
>
>     2017-08-03 21:00:19 GMT LOG:  00000: Verbindung empfangen: Host=[local]
>     2017-08-03 21:00:19 GMT ORT:  BackendInitialize, postmaster.c:4135
>     2017-08-03 21:00:19 GMT LOG:  00000: Verbindung autorisiert: Benutzer=gm-dbo Datenbank=gnumed_v20
>     2017-08-03 21:00:19 GMT ORT:  PerformAuthentication, postinit.c:272
>     2017-08-03 21:00:19 GMT LOG:  00000: statement: SELECT upper(source), name, upper(setting) FROM pg_settings WHERE
name= 'default_transaction_read_only' 
>     2017-08-03 21:00:19 GMT LOCATION:  exec_simple_query, postgres.c:935
>     2017-08-03 21:00:19 GMT LOG:  00000: statement: create database gnumed_v20_copy template gnumed_v20
>     2017-08-03 21:00:19 GMT LOCATION:  exec_simple_query, postgres.c:935
>     2017-08-03 21:00:19 GMT ERROR:  25006: cannot execute CREATE DATABASE in a read-only transaction
>     2017-08-03 21:00:19 GMT LOCATION:  PreventCommandIfReadOnly, utility.c:236
>     2017-08-03 21:00:19 GMT STATEMENT:  create database gnumed_v20_copy template gnumed_v20
>     2017-08-03 21:00:19 GMT LOG:  00000: disconnection: session time: 0:00:00.007 user=gm-dbo database=gnumed_v20
host=[local]
>     2017-08-03 21:00:19 GMT LOCATION:  log_disconnections, postgres.c:4501
>
> Commenting out the marked line makes it work and the log shows:
>
>     2017-08-03 21:01:24 GMT LOG:  00000: Verbindung empfangen: Host=[local]
>     2017-08-03 21:01:24 GMT ORT:  BackendInitialize, postmaster.c:4135
>     2017-08-03 21:01:24 GMT LOG:  00000: Verbindung autorisiert: Benutzer=postgres Datenbank=postgres
>     2017-08-03 21:01:24 GMT ORT:  PerformAuthentication, postinit.c:272
>     2017-08-03 21:01:25 GMT LOG:  00000: Anweisung: drop database gnumed_v20_copy ;
>     2017-08-03 21:01:25 GMT ORT:  exec_simple_query, postgres.c:935
>     2017-08-03 21:01:26 GMT LOG:  00000: Verbindungsende: Sitzungszeit: 0:00:02.324 Benutzer=postgres
Datenbank=postgresHost=[local] 
>     2017-08-03 21:01:26 GMT ORT:  log_disconnections, postgres.c:4501
>     2017-08-03 21:01:33 GMT LOG:  00000: Verbindung empfangen: Host=[local]
>     2017-08-03 21:01:33 GMT ORT:  BackendInitialize, postmaster.c:4135
>     2017-08-03 21:01:33 GMT LOG:  00000: Verbindung autorisiert: Benutzer=gm-dbo Datenbank=gnumed_v20
>     2017-08-03 21:01:33 GMT ORT:  PerformAuthentication, postinit.c:272
>     2017-08-03 21:01:33 GMT LOG:  00000: statement: SET default_transaction_read_only TO 'off'
>     2017-08-03 21:01:33 GMT LOCATION:  exec_simple_query, postgres.c:935
>     2017-08-03 21:01:33 GMT LOG:  00000: statement: SELECT upper(source), name, upper(setting) FROM pg_settings WHERE
name= 'default_transaction_read_only' 
>     2017-08-03 21:01:33 GMT LOCATION:  exec_simple_query, postgres.c:935
>     2017-08-03 21:01:33 GMT LOG:  00000: statement: create database gnumed_v20_copy template gnumed_v20
>     2017-08-03 21:01:33 GMT LOCATION:  exec_simple_query, postgres.c:935
>     2017-08-03 21:01:37 GMT LOG:  00000: duration: 3447.360 ms
>     2017-08-03 21:01:37 GMT LOCATION:  exec_simple_query, postgres.c:1164
>     2017-08-03 21:01:37 GMT LOG:  00000: disconnection: session time: 0:00:03.454 user=gm-dbo database=gnumed_v20
host=[local]
>     2017-08-03 21:01:37 GMT LOCATION:  log_disconnections, postgres.c:4501
>
> So, under some circumstances psycopg2 does send the
>
>     2017-08-03 21:01:33 GMT LOG:  00000: statement: SET default_transaction_read_only TO 'off'
>
> while sometimes it does not, despite both times being in
> autocommit=True and readonly=False when the "create database"
> is sent.
>
> Of course, the
>
>     conn.autocommit = False
>     conn.readonly = False                            # <========== comment out for success
>     conn.autocommit = True
>     conn.readonly = False
>
> seems redundant but that is the relevant sequence of events
> condensed from a larger body of code. Redundant as it may
> seem it should still work, no ?
>
> I am not sure I fully grok what is happening...
>
> Thanks for insights,
> Karsten
>
>
> On Wed, Aug 02, 2017 at 09:38:21PM +0200, Karsten Hilbert wrote:
>
> > On Wed, Aug 02, 2017 at 03:10:15PM +0100, Daniele Varrazzo wrote:
> >
> > > If you want to be absolutely sure about what the adapter does I
> > > suggest you to enable statements log on the server and check what
> > > statements are produced by psycopg. If you find any behaviour
> > > inconsistent with what documented please let us know.
> >
> > One thing I noticed is that it seems pycopg2 does not send a
> > "set default_transaction_read_only to off" when
> >
> > - a database had been created and ALTERed to permanently
> >   be "default_transaction_read_only to ON"
> >
> > - a transaction had been opened on a connection with
> >
> >         BEGIN ISOLATION LEVEL SERIALIZABLE READ WRITE
> >
> >   in order to run various comments (such as setting
> >   default client timezone)
> >
> > - that transaction had been committed
> >
> > - the connection is switched to autocommit=true
> >     (for running DROP DATABASE et al)
> >
> >         2017-08-02 19:18:13 GMT LOG:  00000: Verbindung empfangen: Host=[local]
> >         2017-08-02 19:18:13 GMT ORT:  BackendInitialize, postmaster.c:4135
> >         2017-08-02 19:18:13 GMT LOG:  00000: Verbindung autorisiert: Benutzer=postgres Datenbank=gnumed_v20
> >         2017-08-02 19:18:13 GMT ORT:  PerformAuthentication, postinit.c:272
> >         2017-08-02 19:18:13 GMT LOG:  00000: statement: BEGIN ISOLATION LEVEL SERIALIZABLE READ WRITE
> >         2017-08-02 19:18:13 GMT LOCATION:  exec_simple_query, postgres.c:935
> >         2017-08-02 19:18:13 GMT LOG:  00000: statement: set timezone to 'Europe/Madrid'
> >         2017-08-02 19:18:13 GMT LOCATION:  exec_simple_query, postgres.c:935
> >         2017-08-02 19:18:13 GMT LOG:  00000: statement: COMMIT
> >         2017-08-02 19:18:13 GMT LOCATION:  exec_simple_query, postgres.c:935
> >         2017-08-02 19:18:13 GMT LOG:  00000: statement: BEGIN ISOLATION LEVEL SERIALIZABLE READ WRITE
> >         2017-08-02 19:18:13 GMT LOCATION:  exec_simple_query, postgres.c:935
> >         2017-08-02 19:18:13 GMT LOG:  00000: statement: set lc_messages to 'C'
> >         2017-08-02 19:18:13 GMT LOCATION:  exec_simple_query, postgres.c:935
> >         2017-08-02 19:18:13 GMT LOG:  00000: statement: select md5(gm.concat_table_structure(20::integer)) as md5
> >         2017-08-02 19:18:13 GMT LOCATION:  exec_simple_query, postgres.c:935
> >         2017-08-02 19:18:14 GMT LOG:  00000: duration: 1156.407 ms
> >         2017-08-02 19:18:14 GMT LOCATION:  exec_simple_query, postgres.c:1164
> >         2017-08-02 19:18:14 GMT LOG:  00000: statement: SELECT datname FROM pg_database WHERE datname='gnumed_v21'
> >         2017-08-02 19:18:14 GMT LOCATION:  exec_simple_query, postgres.c:935
> >         2017-08-02 19:18:14 GMT LOG:  00000: statement: COMMIT
> >         2017-08-02 19:18:14 GMT LOCATION:  exec_simple_query, postgres.c:935
> >         2017-08-02 19:18:14 GMT LOG:  00000: statement: DROP DATABASE "gnumed_v21"
> >         2017-08-02 19:18:14 GMT LOCATION:  exec_simple_query, postgres.c:935
> >         2017-08-02 19:18:14 GMT ERROR:  25006: cannot execute DROP DATABASE in a read-only transaction
> >         2017-08-02 19:18:14 GMT LOCATION:  PreventCommandIfReadOnly, utility.c:236
> >         2017-08-02 19:18:14 GMT STATEMENT:  DROP DATABASE "gnumed_v21"
> >         2017-08-02 19:18:14 GMT LOG:  00000: statement: SET default_transaction_isolation TO DEFAULT
> >         2017-08-02 19:18:14 GMT LOCATION:  exec_simple_query, postgres.c:935
> >         2017-08-02 19:18:14 GMT LOG:  00000: statement: SET default_transaction_read_only TO DEFAULT
> >         2017-08-02 19:18:14 GMT LOCATION:  exec_simple_query, postgres.c:935
> >         2017-08-02 19:18:14 GMT LOG:  00000: disconnection: session time: 0:00:01.183 user=postgres
database=gnumed_v20host=[local] 
> >         2017-08-02 19:18:14 GMT LOCATION:  log_disconnections, postgres.c:4501
> >
> > The only other indirect proof I have for this is: if I insert
> > a manual "set default_transaction_read_only to off" right
> > before the "drop database ..." (but after having been
> > switched to autocommit) it works as expected.
> >
> > If the connection has NOT been used for any other
> > transactions (that is, if it is switched to autocommit=true
> > and then to readonly=off) it also works as expected -
> > psycopg2 sends a "set default_transaction_read_only to off".
> >
> > Karsten
> > --
> > GPG key ID E4071346 @ eu.pool.sks-keyservers.net
> > E167 67FD A291 2BEA 73BD  4537 78B9 A9F9 E407 1346
> >
> >
> > --
> > Sent via psycopg mailing list (psycopg@postgresql.org)
> > To make changes to your subscription:
> > http://www.postgresql.org/mailpref/psycopg
>
> --
> GPG key ID E4071346 @ eu.pool.sks-keyservers.net
> E167 67FD A291 2BEA 73BD  4537 78B9 A9F9 E407 1346

>
> db = u'gnumed_v20'        # a database configured "alter database %s set default_transaction_read_only to on"
> user = 'gm-dbo'            # a user with CREATE DATABASE powers
>
>
>
> cmd_def_tx_ro = "SELECT upper(source), name, upper(setting) FROM pg_settings WHERE name =
'default_transaction_read_only'"
> cmd_create_db = "create database %s_copy template %s" % (db, db)
>
>
> import sys
> import psycopg2
>
>
> conn = psycopg2.connect(dbname = db, user = user)
> print 'conn:', conn
> print 'readonly:', conn.readonly
> print 'autocommit:', conn.autocommit
> conn.autocommit = False
> conn.readonly = False                    # <============ comment out for success
> conn.autocommit = True
> print 'autocommit now:', conn.autocommit
> conn.readonly = False
> print 'readonly now:', conn.readonly
> curs = conn.cursor()
> curs.execute(cmd_def_tx_ro)
> print 'should show DEFAULT_TRANSACTION_READ_ONLY set to ON'
> print curs.fetchall()
> curs.close()
> conn.commit()
> print 'the following CREATE DATABASE should fail'
> curs = conn.cursor()
> try:
>     curs.execute(cmd_create_db)
> except psycopg2.InternalError as ex:
>     print 'SQL failed:'
>     print ex
>
> print 'shutting down'
>
> curs.close()
> conn.rollback()
> conn.close()

>
> --
> Sent via psycopg mailing list (psycopg@postgresql.org)
> To make changes to your subscription:
> http://www.postgresql.org/mailpref/psycopg


--
GPG key ID E4071346 @ eu.pool.sks-keyservers.net
E167 67FD A291 2BEA 73BD  4537 78B9 A9F9 E407 1346


[psycopg] 2.7.2 still problem with readonly/autocommit, was: Changingset_session implementation

От
Karsten Hilbert
Дата:
While the below tests had been run with 2.7.1 the problem
persists with 2.7.3.

I am again attaching the script for reproducing the problem.

Can anyone test, reproduce, suggest a fix ?

Thanks,
Karsten


On Mon, Aug 07, 2017 at 10:35:26PM +0200, Karsten Hilbert wrote:

> has anyone been able to reproduce the problem described
> below?  I'd be glad to provide more information if needed.
>
> Thanks !
>
> Karsten
>
>
> On Thu, Aug 03, 2017 at 11:08:18PM +0200, Karsten Hilbert wrote:
>
> > Regarding the below problem with readwrite autocommit
> > connections in default-readonly databases I have whittled
> > down a test case to the following (attached):
> >
> >     db = u'gnumed_v20'        # a database configured "alter database %s set default_transaction_read_only to on"
> >     user = 'gm-dbo'            # a user with CREATE DATABASE powers
> >
> >     cmd_def_tx_ro = "SELECT upper(source), name, upper(setting) FROM pg_settings WHERE name =
'default_transaction_read_only'"
> >     cmd_create_db = "create database %s_copy template %s" % (db, db)
> >
> >     import sys
> >     import psycopg2
> >
> >     conn = psycopg2.connect(dbname = db, user = user)
> >     print 'conn:', conn
> >     print 'readonly:', conn.readonly
> >     print 'autocommit:', conn.autocommit
> >     conn.autocommit = False
> >     conn.readonly = False                            # <========== comment out for success
> >     conn.autocommit = True
> >     print 'autocommit now:', conn.autocommit
> >     conn.readonly = False
> >     print 'readonly now:', conn.readonly
> >     curs = conn.cursor()
> >     curs.execute(cmd_def_tx_ro)
> >     print 'should show DEFAULT_TRANSACTION_READ_ONLY set to ON'
> >     print curs.fetchall()
> >     curs.close()
> >     conn.commit()
> >     print 'the following CREATE DATABASE should fail'
> >     curs = conn.cursor()
> >     try:
> >         curs.execute(cmd_create_db)
> >     except psycopg2.InternalError as ex:
> >         print 'SQL failed:'
> >         print ex
> >
> >     print 'shutting down'
> >
> >     curs.close()
> >     conn.rollback()
> >     conn.close()
> >
> > which shows on the console:
> >
> >     ncq@hermes:~$ python x-test-default_ro.py
> >     conn: <connection object at 0xb674e6bc; dsn: 'dbname=gnumed_v20 user=gm-dbo', closed: 0>
> >     readonly: None
> >     autocommit: False
> >     autocommit now: True
> >     readonly now: False
> >     should show DEFAULT_TRANSACTION_READ_ONLY set to ON
> >     [('DATABASE', 'default_transaction_read_only', 'ON')]
> >     the following CREATE DATABASE should fail
> >     SQL failed:
> >     cannot execute CREATE DATABASE in a read-only transaction
> >
> >     shutting down
> >
> > The PostgreSQL log shows:
> >
> >     2017-08-03 21:00:19 GMT LOG:  00000: Verbindung empfangen: Host=[local]
> >     2017-08-03 21:00:19 GMT ORT:  BackendInitialize, postmaster.c:4135
> >     2017-08-03 21:00:19 GMT LOG:  00000: Verbindung autorisiert: Benutzer=gm-dbo Datenbank=gnumed_v20
> >     2017-08-03 21:00:19 GMT ORT:  PerformAuthentication, postinit.c:272
> >     2017-08-03 21:00:19 GMT LOG:  00000: statement: SELECT upper(source), name, upper(setting) FROM pg_settings
WHEREname = 'default_transaction_read_only' 
> >     2017-08-03 21:00:19 GMT LOCATION:  exec_simple_query, postgres.c:935
> >     2017-08-03 21:00:19 GMT LOG:  00000: statement: create database gnumed_v20_copy template gnumed_v20
> >     2017-08-03 21:00:19 GMT LOCATION:  exec_simple_query, postgres.c:935
> >     2017-08-03 21:00:19 GMT ERROR:  25006: cannot execute CREATE DATABASE in a read-only transaction
> >     2017-08-03 21:00:19 GMT LOCATION:  PreventCommandIfReadOnly, utility.c:236
> >     2017-08-03 21:00:19 GMT STATEMENT:  create database gnumed_v20_copy template gnumed_v20
> >     2017-08-03 21:00:19 GMT LOG:  00000: disconnection: session time: 0:00:00.007 user=gm-dbo database=gnumed_v20
host=[local]
> >     2017-08-03 21:00:19 GMT LOCATION:  log_disconnections, postgres.c:4501
> >
> > Commenting out the marked line makes it work and the log shows:
> >
> >     2017-08-03 21:01:24 GMT LOG:  00000: Verbindung empfangen: Host=[local]
> >     2017-08-03 21:01:24 GMT ORT:  BackendInitialize, postmaster.c:4135
> >     2017-08-03 21:01:24 GMT LOG:  00000: Verbindung autorisiert: Benutzer=postgres Datenbank=postgres
> >     2017-08-03 21:01:24 GMT ORT:  PerformAuthentication, postinit.c:272
> >     2017-08-03 21:01:25 GMT LOG:  00000: Anweisung: drop database gnumed_v20_copy ;
> >     2017-08-03 21:01:25 GMT ORT:  exec_simple_query, postgres.c:935
> >     2017-08-03 21:01:26 GMT LOG:  00000: Verbindungsende: Sitzungszeit: 0:00:02.324 Benutzer=postgres
Datenbank=postgresHost=[local] 
> >     2017-08-03 21:01:26 GMT ORT:  log_disconnections, postgres.c:4501
> >     2017-08-03 21:01:33 GMT LOG:  00000: Verbindung empfangen: Host=[local]
> >     2017-08-03 21:01:33 GMT ORT:  BackendInitialize, postmaster.c:4135
> >     2017-08-03 21:01:33 GMT LOG:  00000: Verbindung autorisiert: Benutzer=gm-dbo Datenbank=gnumed_v20
> >     2017-08-03 21:01:33 GMT ORT:  PerformAuthentication, postinit.c:272
> >     2017-08-03 21:01:33 GMT LOG:  00000: statement: SET default_transaction_read_only TO 'off'
> >     2017-08-03 21:01:33 GMT LOCATION:  exec_simple_query, postgres.c:935
> >     2017-08-03 21:01:33 GMT LOG:  00000: statement: SELECT upper(source), name, upper(setting) FROM pg_settings
WHEREname = 'default_transaction_read_only' 
> >     2017-08-03 21:01:33 GMT LOCATION:  exec_simple_query, postgres.c:935
> >     2017-08-03 21:01:33 GMT LOG:  00000: statement: create database gnumed_v20_copy template gnumed_v20
> >     2017-08-03 21:01:33 GMT LOCATION:  exec_simple_query, postgres.c:935
> >     2017-08-03 21:01:37 GMT LOG:  00000: duration: 3447.360 ms
> >     2017-08-03 21:01:37 GMT LOCATION:  exec_simple_query, postgres.c:1164
> >     2017-08-03 21:01:37 GMT LOG:  00000: disconnection: session time: 0:00:03.454 user=gm-dbo database=gnumed_v20
host=[local]
> >     2017-08-03 21:01:37 GMT LOCATION:  log_disconnections, postgres.c:4501
> >
> > So, under some circumstances psycopg2 does send the
> >
> >     2017-08-03 21:01:33 GMT LOG:  00000: statement: SET default_transaction_read_only TO 'off'
> >
> > while sometimes it does not, despite both times being in
> > autocommit=True and readonly=False when the "create database"
> > is sent.
> >
> > Of course, the
> >
> >     conn.autocommit = False
> >     conn.readonly = False                            # <========== comment out for success
> >     conn.autocommit = True
> >     conn.readonly = False
> >
> > seems redundant but that is the relevant sequence of events
> > condensed from a larger body of code. Redundant as it may
> > seem it should still work, no ?
> >
> > I am not sure I fully grok what is happening...
> >
> > Thanks for insights,
> > Karsten
> >
> >
> > On Wed, Aug 02, 2017 at 09:38:21PM +0200, Karsten Hilbert wrote:
> >
> > > On Wed, Aug 02, 2017 at 03:10:15PM +0100, Daniele Varrazzo wrote:
> > >
> > > > If you want to be absolutely sure about what the adapter does I
> > > > suggest you to enable statements log on the server and check what
> > > > statements are produced by psycopg. If you find any behaviour
> > > > inconsistent with what documented please let us know.
> > >
> > > One thing I noticed is that it seems pycopg2 does not send a
> > > "set default_transaction_read_only to off" when
> > >
> > > - a database had been created and ALTERed to permanently
> > >   be "default_transaction_read_only to ON"
> > >
> > > - a transaction had been opened on a connection with
> > >
> > >         BEGIN ISOLATION LEVEL SERIALIZABLE READ WRITE
> > >
> > >   in order to run various comments (such as setting
> > >   default client timezone)
> > >
> > > - that transaction had been committed
> > >
> > > - the connection is switched to autocommit=true
> > >     (for running DROP DATABASE et al)
> > >
> > >         2017-08-02 19:18:13 GMT LOG:  00000: Verbindung empfangen: Host=[local]
> > >         2017-08-02 19:18:13 GMT ORT:  BackendInitialize, postmaster.c:4135
> > >         2017-08-02 19:18:13 GMT LOG:  00000: Verbindung autorisiert: Benutzer=postgres Datenbank=gnumed_v20
> > >         2017-08-02 19:18:13 GMT ORT:  PerformAuthentication, postinit.c:272
> > >         2017-08-02 19:18:13 GMT LOG:  00000: statement: BEGIN ISOLATION LEVEL SERIALIZABLE READ WRITE
> > >         2017-08-02 19:18:13 GMT LOCATION:  exec_simple_query, postgres.c:935
> > >         2017-08-02 19:18:13 GMT LOG:  00000: statement: set timezone to 'Europe/Madrid'
> > >         2017-08-02 19:18:13 GMT LOCATION:  exec_simple_query, postgres.c:935
> > >         2017-08-02 19:18:13 GMT LOG:  00000: statement: COMMIT
> > >         2017-08-02 19:18:13 GMT LOCATION:  exec_simple_query, postgres.c:935
> > >         2017-08-02 19:18:13 GMT LOG:  00000: statement: BEGIN ISOLATION LEVEL SERIALIZABLE READ WRITE
> > >         2017-08-02 19:18:13 GMT LOCATION:  exec_simple_query, postgres.c:935
> > >         2017-08-02 19:18:13 GMT LOG:  00000: statement: set lc_messages to 'C'
> > >         2017-08-02 19:18:13 GMT LOCATION:  exec_simple_query, postgres.c:935
> > >         2017-08-02 19:18:13 GMT LOG:  00000: statement: select md5(gm.concat_table_structure(20::integer)) as md5
> > >         2017-08-02 19:18:13 GMT LOCATION:  exec_simple_query, postgres.c:935
> > >         2017-08-02 19:18:14 GMT LOG:  00000: duration: 1156.407 ms
> > >         2017-08-02 19:18:14 GMT LOCATION:  exec_simple_query, postgres.c:1164
> > >         2017-08-02 19:18:14 GMT LOG:  00000: statement: SELECT datname FROM pg_database WHERE
datname='gnumed_v21'
> > >         2017-08-02 19:18:14 GMT LOCATION:  exec_simple_query, postgres.c:935
> > >         2017-08-02 19:18:14 GMT LOG:  00000: statement: COMMIT
> > >         2017-08-02 19:18:14 GMT LOCATION:  exec_simple_query, postgres.c:935
> > >         2017-08-02 19:18:14 GMT LOG:  00000: statement: DROP DATABASE "gnumed_v21"
> > >         2017-08-02 19:18:14 GMT LOCATION:  exec_simple_query, postgres.c:935
> > >         2017-08-02 19:18:14 GMT ERROR:  25006: cannot execute DROP DATABASE in a read-only transaction
> > >         2017-08-02 19:18:14 GMT LOCATION:  PreventCommandIfReadOnly, utility.c:236
> > >         2017-08-02 19:18:14 GMT STATEMENT:  DROP DATABASE "gnumed_v21"
> > >         2017-08-02 19:18:14 GMT LOG:  00000: statement: SET default_transaction_isolation TO DEFAULT
> > >         2017-08-02 19:18:14 GMT LOCATION:  exec_simple_query, postgres.c:935
> > >         2017-08-02 19:18:14 GMT LOG:  00000: statement: SET default_transaction_read_only TO DEFAULT
> > >         2017-08-02 19:18:14 GMT LOCATION:  exec_simple_query, postgres.c:935
> > >         2017-08-02 19:18:14 GMT LOG:  00000: disconnection: session time: 0:00:01.183 user=postgres
database=gnumed_v20host=[local] 
> > >         2017-08-02 19:18:14 GMT LOCATION:  log_disconnections, postgres.c:4501
> > >
> > > The only other indirect proof I have for this is: if I insert
> > > a manual "set default_transaction_read_only to off" right
> > > before the "drop database ..." (but after having been
> > > switched to autocommit) it works as expected.
> > >
> > > If the connection has NOT been used for any other
> > > transactions (that is, if it is switched to autocommit=true
> > > and then to readonly=off) it also works as expected -
> > > psycopg2 sends a "set default_transaction_read_only to off".
> > >
> > > Karsten
> > > --
> > > GPG key ID E4071346 @ eu.pool.sks-keyservers.net
> > > E167 67FD A291 2BEA 73BD  4537 78B9 A9F9 E407 1346
> > >
> > >
> > > --
> > > Sent via psycopg mailing list (psycopg@postgresql.org)
> > > To make changes to your subscription:
> > > http://www.postgresql.org/mailpref/psycopg
> >
> > --
> > GPG key ID E4071346 @ eu.pool.sks-keyservers.net
> > E167 67FD A291 2BEA 73BD  4537 78B9 A9F9 E407 1346
--
GPG key ID E4071346 @ eu.pool.sks-keyservers.net
E167 67FD A291 2BEA 73BD  4537 78B9 A9F9 E407 1346

Вложения

Re: [psycopg] 2.7.2 still problem with readonly/autocommit, was:Changing set_session implementation

От
Daniele Varrazzo
Дата:
On Thu, Aug 10, 2017 at 1:45 PM, Karsten Hilbert
<Karsten.Hilbert@gmx.net> wrote:
> While the below tests had been run with 2.7.1 the problem
> persists with 2.7.3.
>
> I am again attaching the script for reproducing the problem.
>
> Can anyone test, reproduce, suggest a fix ?

Hi Karsten

Building psycopg in debug mode gives you all the information to infer
its behaviour. Here are redacted outputs.

You want to set a connection in autocommit and change a a transaction
parameter to a non default. If you change parameter in a non
autocommit database the state will be kept in the python object and
applied at begin time:

>>> conn = psycopg2.connect('')
[26725] psyco_connect: dsn = '', async = 0

>>> cur = conn.cursor()

>>> cur.execute("select 1")
[26725] pq_execute_command_locked: pgconn = 0x902a6f0, query = BEGIN
[26725] pq_execute: executing SYNC query: pgconn = 0x902a6f0
[26725]     select 1

>>> conn.rollback()

>>> conn.readonly = False
[26725] conn_set_session: autocommit 0, isolevel 5, readonly 0, deferrable 2

>>> cur.execute("select 1")
[26725] pq_execute_command_locked: pgconn = 0x902a6f0, query = BEGIN READ WRITE
[26725] pq_execute: executing SYNC query: pgconn = 0x902a6f0
[26725]     select 1

>>> conn.rollback()


If instead you set the connection in autocommit and try changing the
session state psycopg will change the session state. This is with a
database defaulting to readonly:

>>> conn = psycopg2.connect('')
[26725] psyco_connect: dsn = '', async = 0

>>> conn.autocommit = True
[26725] conn_set_session: autocommit 1, isolevel 5, readonly 2, deferrable 2

>>> conn.readonly = False
[26725] pq_set_guc_locked: setting default_transaction_read_only to off
[26725] pq_execute_command_locked: pgconn = 0x8f84280, query = SET
default_transaction_read_only TO 'off'
[26725] conn_set_session: autocommit 1, isolevel 5, readonly 0, deferrable 2

>>> cur = conn.cursor()
>>> cur.execute("create database k")
[26725] pq_execute: executing SYNC query: pgconn = 0x8f84280
[26725]     create database k

>>> cur.statusmessage
'CREATE DATABASE'

As you can see the SET statement is issued *on setting the readonly
property* when the connection is autocommit. What will not happen is
psycopg setting the readonly state when switching autocommit, copying
it from the internal state to the session state. It won't because this
would be run three queries when switching to autocommit=True; implicit
operations in unexpected moments usually spell trouble.

This should be enough to work around your problem: put the connection
in read-write state *after* setting autocommit, or execute "SET
default_transaction_read_only TO 'off'" manually before creating the
database if for some reason in your workflow you cannot do that.

Hope this helps. Feel free to propose a merge request with change in
documentation if you find it would have helped.


-- Daniele


Re: [psycopg] 2.7.2 still problem with readonly/autocommit, was:Changing set_session implementation

От
Karsten Hilbert
Дата:
On Fri, Aug 11, 2017 at 12:22:55AM +0100, Daniele Varrazzo wrote:

> Building psycopg in debug mode gives you all the information to infer
> its behaviour. Here are redacted outputs.
>
> You want to set a connection in autocommit and change a a transaction
> parameter to a non default. If you change parameter in a non
> autocommit database the state will be kept in the python object and
> applied at begin time:
>
> >>> conn = psycopg2.connect('')
> [26725] psyco_connect: dsn = '', async = 0
>
> >>> cur = conn.cursor()
>
> >>> cur.execute("select 1")
> [26725] pq_execute_command_locked: pgconn = 0x902a6f0, query = BEGIN
> [26725] pq_execute: executing SYNC query: pgconn = 0x902a6f0
> [26725]     select 1
>
> >>> conn.rollback()
>
> >>> conn.readonly = False
> [26725] conn_set_session: autocommit 0, isolevel 5, readonly 0, deferrable 2
>
> >>> cur.execute("select 1")
> [26725] pq_execute_command_locked: pgconn = 0x902a6f0, query = BEGIN READ WRITE
> [26725] pq_execute: executing SYNC query: pgconn = 0x902a6f0
> [26725]     select 1
>
> >>> conn.rollback()
>
>
> If instead you set the connection in autocommit and try changing the
> session state psycopg will change the session state. This is with a
> database defaulting to readonly:
>
> >>> conn = psycopg2.connect('')
> [26725] psyco_connect: dsn = '', async = 0
>
> >>> conn.autocommit = True
> [26725] conn_set_session: autocommit 1, isolevel 5, readonly 2, deferrable 2
>
> >>> conn.readonly = False
> [26725] pq_set_guc_locked: setting default_transaction_read_only to off
> [26725] pq_execute_command_locked: pgconn = 0x8f84280, query = SET
> default_transaction_read_only TO 'off'
> [26725] conn_set_session: autocommit 1, isolevel 5, readonly 0, deferrable 2
>
> >>> cur = conn.cursor()
> >>> cur.execute("create database k")
> [26725] pq_execute: executing SYNC query: pgconn = 0x8f84280
> [26725]     create database k
>
> >>> cur.statusmessage
> 'CREATE DATABASE'
>
> As you can see the SET statement is issued *on setting the readonly
> property* when the connection is autocommit. What will not happen is
> psycopg setting the readonly state when switching autocommit, copying
> it from the internal state to the session state. It won't because this
> would be run three queries when switching to autocommit=True; implicit
> operations in unexpected moments usually spell trouble.

I agree with this rationale and I understand your
explanation. Thanks for taking the time. What I am saying,
however, is that I think I have found a bug. Notice this
sequence in my test script:

    conn.autocommit = False
    conn.readonly = False
    conn.autocommit = True
    conn.readonly = False

After this I would assume the connection to be in readwrite
mode (per your explanation, psycopg2 would (also) emit a "set
default_trans...off" when the last readonly=False is given.

However, it does NOT, as the PostgreSQL log snippet shows.

Here is the console output (I will check whether I can get
access to a debug build):

    conn: <connection object at 0xb66a88b4; dsn: 'dbname=gnumed_v20 user=gm-dbo', closed: 0>
    readonly: None
    autocommit: False
    setting autocommit to False
    autocommit now: False
    setting readonly to False
    readonly now: False
    setting autocommit to True
    autocommit now: True
    readonly now: False
    querying DEFAULT_TRANSACTION_READ_ONLY state (should show "ON")
    [('DATABASE', 'default_transaction_read_only', 'ON')]
    the following SQL will fail: create database gnumed_v20_copy template gnumed_v20
    (note that the transaction being talked about is implicit to PostgreSQL, due to autocommit mode)
    SQL failed:
    cannot execute CREATE DATABASE in a read-only transaction
    shutting down

If I comment out the *first* call to readonly=False (after
autocommit=False, before autocommit=True) it does work.

It seems as if psycopg2 "remembers" that it has already
called readonly=False.

Here is the script again:


db = u'gnumed_v20'        # a database configured "alter database %s set default_transaction_read_only to on"
user = 'gm-dbo'            # a user with CREATE DATABASE powers
cmd_def_tx_ro = "SELECT upper(source), name, upper(setting) FROM pg_settings WHERE name =
'default_transaction_read_only'"
cmd_create_db = "create database %s_copy template %s" % (db, db)
import sys
import psycopg2
conn = psycopg2.connect(dbname = db, user = user)
print 'conn:', conn
print 'readonly:', conn.readonly
print 'autocommit:', conn.autocommit
print 'setting autocommit to False'
conn.autocommit = False
print 'autocommit now:', conn.autocommit
print 'setting readonly to False'
conn.readonly = False                            # comment out for success
print 'readonly now:', conn.readonly
print 'setting autocommit to True'
conn.autocommit = True
print 'autocommit now:', conn.autocommit
conn.readonly = False
print 'readonly now:', conn.readonly
curs = conn.cursor()
curs.execute(cmd_def_tx_ro)
print 'querying DEFAULT_TRANSACTION_READ_ONLY state (should show "ON")'
print curs.fetchall()
curs.close()
conn.commit()
print 'the following SQL will fail:', cmd_create_db
print '(note that the transaction being talked about is implicit to PostgreSQL, due to autocommit mode)'
curs = conn.cursor()
try:
    curs.execute(cmd_create_db)
except psycopg2.InternalError as ex:
    print 'SQL failed:'
    print ex
print 'shutting down'
curs.close()
conn.rollback()
conn.close()

> This should be enough to work around your problem: put the connection
> in read-write state *after* setting autocommit

I do, as you can see from the script above.

> or execute "SET
> default_transaction_read_only TO 'off'" manually before creating the
> database if for some reason in your workflow you cannot do that.

This is the workaround I am currently using.
I still wonder why the above does not work.

> Hope this helps. Feel free to propose a merge request with change in
> documentation if you find it would have helped.

At this point I am still unsure whether the above is a bug.

The documentation change would amount to something like

    once in autocommit mode do not rely on psycopg2 methods
    for switching readonly on or off, do not even rely on
    what the psycopg2 connection object _tells_ you about the
    state it is in

While it seems true at the moment I don't think this goes
along with the overall excellent quality of psycopg2 :-)

May I kindly ask you to run my script against a readonly
database ?  The particular dance between autocommit/readonly
on/off is needed to show the problem.

Meanwhile I will try to somehow run a debug build.

Thanks for thinking along,
Karsten
--
GPG key ID E4071346 @ eu.pool.sks-keyservers.net
E167 67FD A291 2BEA 73BD  4537 78B9 A9F9 E407 1346


Re: [psycopg] 2.7.2 still problem with readonly/autocommit, was:Changing set_session implementation

От
Karsten Hilbert
Дата:
> Here is the script again:

Improved and attached for convenience.

Replace database name and user name inside it.

Run it with or without "show_problem" as the first and only
command line argument.

> Meanwhile I will try to somehow run a debug build.

Debian does not offer a debug build as far as I can tell and,
consequently, setting

    PSYCOPG_DEBUG=1

does not have any effect.

Karsten
--
GPG key ID E4071346 @ eu.pool.sks-keyservers.net
E167 67FD A291 2BEA 73BD  4537 78B9 A9F9 E407 1346

Вложения

Re: [psycopg] 2.7.2 still problem with readonly/autocommit, was:Changing set_session implementation

От
Daniele Varrazzo
Дата:
On Fri, Aug 11, 2017 at 9:19 AM, Karsten Hilbert
<Karsten.Hilbert@gmx.net> wrote:

> I agree with this rationale and I understand your
> explanation. Thanks for taking the time. What I am saying,
> however, is that I think I have found a bug. Notice this
> sequence in my test script:
>
>         conn.autocommit = False
>         conn.readonly = False
>         conn.autocommit = True
>         conn.readonly = False
>
> After this I would assume the connection to be in readwrite
> mode (per your explanation, psycopg2 would (also) emit a "set
> default_trans...off" when the last readonly=False is given.
>
> However, it does NOT, as the PostgreSQL log snippet shows.

Ok, I see your point, and yes, I think it is a bug.

In [9]: cnn.autocommit = False
[23759] conn_set_session: autocommit 0, isolevel 5, readonly 2, deferrable 2

In [10]: cnn.readonly = True
[23759] conn_set_session: autocommit 0, isolevel 5, readonly 1, deferrable 2

In [11]: cnn.autocommit = True
[23759] conn_set_session: autocommit 1, isolevel 5, readonly 1, deferrable 2

In [12]: cnn.readonly = True
[23759] conn_set_session: autocommit 1, isolevel 5, readonly 1, deferrable 2

The operation in [12] would be idempotent: the code checks that the
new value is the same of the previous one and bails out early. But
checking the internal state for idempotence when in autocommit is
wrong: the only state that matters is what's in the server session.

I'll take a look at the logic of the state switching.

-- Daniele


Re: [psycopg] 2.7.2 still problem with readonly/autocommit, was:Changing set_session implementation

От
Karsten Hilbert
Дата:
On Fri, Aug 11, 2017 at 12:34:35PM +0100, Daniele Varrazzo wrote:

> > After this I would assume the connection to be in readwrite
> > mode (per your explanation, psycopg2 would (also) emit a "set
> > default_trans...off" when the last readonly=False is given.
> >
> > However, it does NOT, as the PostgreSQL log snippet shows.
>
> Ok, I see your point, and yes, I think it is a bug.

Thank you very much and I am sorry for being so stubborn !  :-)

> In [9]: cnn.autocommit = False
> [23759] conn_set_session: autocommit 0, isolevel 5, readonly 2, deferrable 2
>
> In [10]: cnn.readonly = True
> [23759] conn_set_session: autocommit 0, isolevel 5, readonly 1, deferrable 2
>
> In [11]: cnn.autocommit = True
> [23759] conn_set_session: autocommit 1, isolevel 5, readonly 1, deferrable 2
>
> In [12]: cnn.readonly = True
> [23759] conn_set_session: autocommit 1, isolevel 5, readonly 1, deferrable 2
>
> The operation in [12] would be idempotent: the code checks that the
> new value is the same of the previous one and bails out early.

That was my suspicion.

> But
> checking the internal state for idempotence when in autocommit is
> wrong: the only state that matters is what's in the server session.

Aha, I understand !

> I'll take a look at the logic of the state switching.

Thanks !

Karsten
--
GPG key ID E4071346 @ eu.pool.sks-keyservers.net
E167 67FD A291 2BEA 73BD  4537 78B9 A9F9 E407 1346


Re: [psycopg] 2.7.2 still problem with readonly/autocommit, was:Changing set_session implementation

От
Karsten Hilbert
Дата:
Hi Danielle,

I wonder whether there is any news regarding the bug reported
below ?

Thanks a lot,
Karsten

On Fri, Aug 11, 2017 at 03:33:27PM +0200, Karsten Hilbert wrote:

> > > After this I would assume the connection to be in readwrite
> > > mode (per your explanation, psycopg2 would (also) emit a "set
> > > default_trans...off" when the last readonly=False is given.
> > >
> > > However, it does NOT, as the PostgreSQL log snippet shows.
> > 
> > Ok, I see your point, and yes, I think it is a bug.
> 
> Thank you very much and I am sorry for being so stubborn !  :-)
> 
> > In [9]: cnn.autocommit = False
> > [23759] conn_set_session: autocommit 0, isolevel 5, readonly 2, deferrable 2
> > 
> > In [10]: cnn.readonly = True
> > [23759] conn_set_session: autocommit 0, isolevel 5, readonly 1, deferrable 2
> > 
> > In [11]: cnn.autocommit = True
> > [23759] conn_set_session: autocommit 1, isolevel 5, readonly 1, deferrable 2
> > 
> > In [12]: cnn.readonly = True
> > [23759] conn_set_session: autocommit 1, isolevel 5, readonly 1, deferrable 2
> > 
> > The operation in [12] would be idempotent: the code checks that the
> > new value is the same of the previous one and bails out early.
> 
> That was my suspicion.
> 
> > But
> > checking the internal state for idempotence when in autocommit is
> > wrong: the only state that matters is what's in the server session.
> 
> Aha, I understand !
> 
> > I'll take a look at the logic of the state switching.
> 
> Thanks !
> 
> Karsten

-- 
GPG key ID E4071346 @ eu.pool.sks-keyservers.net
E167 67FD A291 2BEA 73BD  4537 78B9 A9F9 E407 1346


-- 
Sent via psycopg mailing list (psycopg@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/psycopg

Re: [psycopg] 2.7.2 still problem with readonly/autocommit, was:Changing set_session implementation

От
Daniele Varrazzo
Дата:
On Fri, Sep 29, 2017 at 10:50 PM, Karsten Hilbert
<Karsten.Hilbert@gmx.net> wrote:

> I wonder whether there is any news regarding the bug reported
> below ?

There is a bug open, no timescale for it. It will be dealt with when I
have time, or earlier if anyone contributes a solution. In honesty it
is not on top of my priority list: even considering the time I can
devolve to free-software projects (which is only a fraction of my free
time), I have more pressing issues in other project (pg_repack
screwing up logical decoding...). And I'm sure not in the position to
use time paid by my employer to fix this bug.

https://github.com/psycopg/psycopg2/issues/580


-- Daniele


-- 
Sent via psycopg mailing list (psycopg@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/psycopg

Re: [psycopg] 2.7.2 still problem with readonly/autocommit, was:Changing set_session implementation

От
Karsten Hilbert
Дата:
On Sun, Oct 01, 2017 at 02:42:33PM +0100, Daniele Varrazzo wrote:

>> I wonder whether there is any news regarding the bug reported
>> below ?
> 
> There is a bug open, no timescale for it. It will be dealt with when I
> have time, or earlier if anyone contributes a solution. In honesty it
> is not on top of my priority list: even considering the time I can
> devolve to free-software projects (which is only a fraction of my free
> time), I have more pressing issues in other project (pg_repack
> screwing up logical decoding...). And I'm sure not in the position to
> use time paid by my employer to fix this bug.
> 
> https://github.com/psycopg/psycopg2/issues/580

Thank you for the clarification.

Karsten
-- 
GPG key ID E4071346 @ eu.pool.sks-keyservers.net
E167 67FD A291 2BEA 73BD  4537 78B9 A9F9 E407 1346


-- 
Sent via psycopg mailing list (psycopg@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/psycopg

Re: [psycopg] 2.7.2 still problem with readonly/autocommit, was:Changing set_session implementation

От
Daniele Varrazzo
Дата:
On Fri, Sep 29, 2017 at 10:50 PM, Karsten Hilbert
<Karsten.Hilbert@gmx.net> wrote:
> Hi Danielle,
>
> I wonder whether there is any news regarding the bug reported
> below ?

Hello Karsten,

FYI I have fixed the bug you reported, in the maint_2_7 and master
branches. Please let me know if you think the adapter now works as
expected.

I would like to release a bugfix 2.7 version in the next week or two.

Best regards,

-- Daniele


Re: [psycopg] 2.7.2 still problem with readonly/autocommit, was:Changing set_session implementation

От
Karsten Hilbert
Дата:
On Thu, Jan 18, 2018 at 11:23:57AM +0000, Daniele Varrazzo wrote:

> FYI I have fixed the bug you reported, in the maint_2_7 and master
> branches. Please let me know if you think the adapter now works as
> expected.

Many thanks for taking care of that issue. From my reading of
the code it should work properly.

Karsten
-- 
GPG key ID E4071346 @ eu.pool.sks-keyservers.net
E167 67FD A291 2BEA 73BD  4537 78B9 A9F9 E407 1346