Jakie są ograniczenia połączeń dla Google Cloud SQL z App Engine i jak najlepiej wykorzystać połączenia DB?
Mam aplikację Google App Engine, która używa instancji Google Cloud SQL do przechowywania danych. Potrzebuję mojej instancji, aby móc obsłużyć setki klientów naraz, za pomocą wywołań spoczynkowych, z których każdy skutkuje jednym lub kilkoma zapytaniami DB. Zapakowałem metody, które wymagają dostępu DB i przechowują uchwyt do połączenia DB w os.environ. Widziećto SO pytanie / odpowiedź w zasadzie jak to robię.
Jednakże, gdy tylko kilkaset klientów połączy się z moją aplikacją i wywoła połączenia z bazą danych, zaczynam uzyskiwać te błędy w dziennikach błędów aplikacji Google App Engine (a moja aplikacja zwraca oczywiście 500):
could not connect: ApplicationError: 1033 Instance has too many concurrent requests: 100 Traceback (most recent call last): File "/base/python27_run
Wszelkie wskazówki od doświadczonych użytkowników Google App Engine i Google Cloud SQL? Z góry dziękuję.
Oto kod dekoratora, którego używam wokół metod wymagających połączenia 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.')