databases icon indicating copy to clipboard operation
databases copied to clipboard

Unable to reconnect database backend after nested transaction exception.

Open ODD2 opened this issue 2 years ago • 1 comments

Issue

Dear Community,

The issue occurs within nested transactions as soon as a short period of disconnection from the database, the databases couldn't automatically reconnect the target database but rather repetitively throws the same exception: InterfaceError("(0, 'Not Connected')").

I'm able to constantly reproduce the issue with a short example code. (Please build a simple MySQL database, configure the example code to connect the database backend on line 8, and fetch arbitrary data from the database on line 27.)

import sys

import asyncio
import databases


api_db = databases.Database(
    "mysql+aiomysql://root:password@mysql-test:3306/testing_db", # the database URL, the selected backend is aiomysql
    min_size=1,
    max_size=1
)


# the nested transaction driver function, the default depth of the nest is 3 layers.
async def driver(layer=2):
    try:
        print(f"{layer}:enter driver")
        async with api_db.transaction(): # start transaction
            print(f"{layer}:start transaciton")
            if (layer > 0):
                print(f"{layer}:enter a transaction nest.")
                await asyncio.sleep(1)
                await driver(layer-1) # enter next depth
            else:
                print(f"{layer}:fetch data from users table.")
                await api_db.fetch_all("SELECT * FROM `users` WHERE 1;") # fetch arbitrary data from databases
                print(f"{layer}:fetch done.")
    except Exception as e:
        raise e
    finally:
        print(f"{layer}:exit driver") 


async def main():
    await api_db.connect() # connect to the database
    while True: 
        try:
            await driver() 
        except Exception as e:
            print(f"exception occured {repr(e)}")
        else:
            print("complete successfully")
        await asyncio.sleep(1)

if __name__ == "__main__":
    asyncio.run(main())

To reproduce the issue, simply disconnect the database backend while the process enters a nested transaction(e.g. when the message "1:enter a transaction nest" prompts in the terminal).

System

OS: 
	- Ubuntu 20.04
PYTHON: 
	- 3.8.16
DATABASES VERSION:
	- 0.7.0
DATABASE BACKEND: 
	- mysql
DATABASE DRIVER: 
	- aiomysql
INSTALLED PACKAGES:
	- aiomysql==0.1.1
	- autopep8 @ file:///opt/conda/conda-bld/autopep8_1650463822033/work
	- certifi @ file:///croot/certifi_1671487769961/work/certifi
	- cffi==1.15.1
	- cryptography==39.0.0
	- databases==0.7.0
	- greenlet==2.0.1
	- pycodestyle @ file:///tmp/build/80754af9/pycodestyle_1636635402688/work
	- pycparser==2.21
	- pydantic==1.10.2
	- PyMySQL==1.0.2
	- SQLAlchemy==1.4.46
	- toml @ file:///tmp/build/80754af9/toml_1616166611790/work
	- typing_extensions==4.4.0

ODD2 avatar Mar 18 '23 07:03 ODD2

This issue seems related to the connection leak problem(#498). I've created a pull request #528 similar to #498, which handles the leak problem during the start of a transaction and returns the raised exceptions.

ODD2 avatar Apr 17 '23 04:04 ODD2