Skip to content

A way to borrow write connection early, before commit() #522

@sjustas

Description

@sjustas

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?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions