¿Cuáles son los límites de conexión para Google Cloud SQL de App Engine y cómo reutilizar mejor las conexiones de base de datos?
Tengo una aplicación Google App Engine que utiliza una instancia de Google Cloud SQL para almacenar datos. Necesito mi instancia para poder atender a cientos de clientes a la vez, a través de llamadas tranquilas, que dan como resultado una o varias consultas de base de datos. Envolví los métodos que necesitan acceso a la base de datos y almacené el identificador de la conexión de la base de datos en os.environ. Veresta SO pregunta / respuesta para básicamente cómo lo estoy haciendo.
Sin embargo, tan pronto como un par de cientos de clientes se conectan a mi aplicación y activan las llamadas a la base de datos, comienzo a recibir estos errores en los registros de errores de Google App Engine (y mi aplicación devuelve 500, por supuesto):
<code>could not connect: ApplicationError: 1033 Instance has too many concurrent requests: 100 Traceback (most recent call last): File "/base/python27_run </code>
¿Algún consejo de usuarios experimentados de Google App Engine y Google Cloud SQL? Gracias por adelantado.
Aquí está el código para el decorador que uso en torno a los métodos que requieren conexión a la base de datos:
<code>def with_db_cursor(do_commit = False): """ Decorator for managing DB connection by wrapping around web calls. Stores connections and open connection count in the os.environ dictionary between calls. Sets a cursor variable in the wrapped function. Optionally does a commit. Closes the cursor when wrapped method returns, and closes the DB connection if there are no outstanding cursors. If the wrapped method has a keyword argument 'existing_cursor', whose value is non-False, this wrapper is bypassed, as it is assumed another cursor is already in force because of an alternate call stack. Based mostly on post by : Shay Erlichmen At: https://stackoverflow.com/a/10162674/379037 """ def method_wrap(method): def wrap(*args, **kwargs): if kwargs.get('existing_cursor', False): #Bypass everything if method called with existing open cursor vdbg('Shortcircuiting db wrapper due to exisiting_cursor') return method(None, *args, **kwargs) conn = os.environ.get("__data_conn") # Recycling connection for the current request # For some reason threading.local() didn't work # and yes os.environ is supposed to be thread safe if not conn: conn = _db_connect() os.environ["__data_conn"] = conn os.environ["__data_conn_ref"] = 1 dbg('Opening first DB connection via wrapper.') else: os.environ["__data_conn_ref"] = (os.environ["__data_conn_ref"] + 1) vdbg('Reusing existing DB connection. Count using is now: {0}', os.environ["__data_conn_ref"]) try: cursor = conn.cursor() try: result = method(cursor, *args, **kwargs) if do_commit or os.environ.get("__data_conn_commit"): os.environ["__data_conn_commit"] = False dbg('Wrapper executing DB commit.') conn.commit() return result finally: cursor.close() finally: os.environ["__data_conn_ref"] = (os.environ["__data_conn_ref"] - 1) vdbg('One less user of DB connection. Count using is now: {0}', os.environ["__data_conn_ref"]) if os.environ["__data_conn_ref"] == 0: dbg("No more users of this DB connection. Closing.") os.environ["__data_conn"] = None db_close(conn) return wrap return method_wrap def db_close(db_conn): if db_conn: try: db_conn.close() except: err('Unable to close the DB connection.', ) raise else: err('Tried to close a non-connected DB handle.') </code>