Ho un'app Google App Engine che utilizza un'istanza di Google Cloud SQL per l'archiviazione dei dati. Ho bisogno che la mia istanza sia in grado di servire centinaia di client alla volta, tramite chiamate restful, che generano ciascuna una o più query DB. Ho avvolto i metodi che richiedono l'accesso ai DB e archiviamo l'handle nella connessione DB in os.environ. Vedi this SO domanda/risposta per fondamentalmente come sto facendo.Quali sono i limiti di connessione per Google Cloud SQL da App Engine e come riutilizzare al meglio le connessioni DB?
Tuttavia, non appena un paio di centinaia di client si connettono al mio app e di trigger chiamate al database, ho iniziare a ricevere questi errori nei log degli errori di Google App Engine (e la mia app restituisce 500, ovviamente):
could not connect: ApplicationError: 1033 Instance has too many concurrent requests: 100 Traceback (most recent call last): File "/base/python27_run
Qualche consiglio da utenti esperti di Google App Engine e Google Cloud SQL? Grazie in anticipo.
Ecco il codice per il decoratore che uso intorno metodi che richiedono connessione DB:
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.')
Hai thread sicuro: vero in app.yaml? – proppy
@proppy sì, lo so. Grazie. – JJC
L'utilizzo di "threadsafe: true" non funzionerà bene con l'utilizzo di os.environ poiché le connessioni non possono essere condivise tra thread. Vedere la mia risposta http://stackoverflow.com/a/10438622/1373093 per una soluzione a prova di thread. –