TL;DR: it would be great to have a method, something like IRelStorage.prepareForWrite(), that would open a store connection and later use it in commit(). This way developers can make sure they'll be able to open store connection before doing non-revertable operations like exchanging OAuth refresh token for a new auth token.
Relstorage uses different connections to load and store objects.
A write connection becomes available only during commit(), when shared state is constructed in tpc_begin phase.
NotInTransaction.tpc_begin() initializes SharedTPCState src/relstorage/storage/tpc/__init__.py#L457, which has a LazyResource property store_connection: src/relstorage/storage/tpc/__init__.py#L134 that calls StoreConnectionPool.borrow() to take a connection from the pool or open a new connection.
Opening a new connection may fail. Since load and store are separate connections, the the transaction may proceed successfully with a load connection right until commit phase where it needs a store connection.
Say you have a sensitive operation that does not support TPC-like behavior. For example, you are trying to refresh OAuth token for an API that does refresh token rotation -- meaning you once you exchange your auth token to a new one, the old one is invalidated immediatelly, and the new one must be stored or access is lost. If you call such API to refresh the token and fail to obtain a write connection, access is lost.
This is more prominent in more complicated setups, where, say, Postgres databases sit behind PgBouncer connection pooler that balances some of read-only connections to a read-only replica database. It is possible that you are far from exhausting read-only connections, but you run out of write connections.
It would be good to have a way to open the write connection before commit() call to make sure we have enough resources. Ideally, opening write connections early would not apply to all transactions, but be on-demand.
It would be great to have some RelStorage method that can be called right before the write-sensitive operation.
Such method could initialize the SharedTPCState state, open the store connection, and store the state on NotInTransaction to be later used in tpc_begin(). Or maybe we could find a way to move the LazyResource store connection to NotInTransaction?
TL;DR: it would be great to have a method, something like
IRelStorage.prepareForWrite(), that would open a store connection and later use it incommit(). This way developers can make sure they'll be able to open store connection before doing non-revertable operations like exchanging OAuth refresh token for a new auth token.Relstorage uses different connections to load and store objects.
A write connection becomes available only during
commit(), when shared state is constructed intpc_beginphase.NotInTransaction.tpc_begin()initializesSharedTPCStatesrc/relstorage/storage/tpc/__init__.py#L457, which has aLazyResourcepropertystore_connection:src/relstorage/storage/tpc/__init__.py#L134that callsStoreConnectionPool.borrow()to take a connection from the pool or open a new connection.Opening a new connection may fail. Since load and store are separate connections, the the transaction may proceed successfully with a load connection right until
commitphase where it needs a store connection.Say you have a sensitive operation that does not support TPC-like behavior. For example, you are trying to refresh OAuth token for an API that does refresh token rotation -- meaning you once you exchange your auth token to a new one, the old one is invalidated immediatelly, and the new one must be stored or access is lost. If you call such API to refresh the token and fail to obtain a write connection, access is lost.
This is more prominent in more complicated setups, where, say, Postgres databases sit behind PgBouncer connection pooler that balances some of read-only connections to a read-only replica database. It is possible that you are far from exhausting read-only connections, but you run out of write connections.
It would be good to have a way to open the write connection before
commit()call to make sure we have enough resources. Ideally, opening write connections early would not apply to all transactions, but be on-demand.It would be great to have some
RelStoragemethod that can be called right before the write-sensitive operation.Such method could initialize the
SharedTPCStatestate, open the store connection, and store the state onNotInTransactionto be later used intpc_begin(). Or maybe we could find a way to move theLazyResourcestore connection toNotInTransaction?