Каковы ограничения на подключение к Google Cloud SQL из App Engine и как лучше повторно использовать подключения к БД?
У меня есть приложение Google App Engine, которое использует экземпляр Google Cloud SQL для хранения данных. Мне нужно, чтобы мой экземпляр мог одновременно обслуживать сотни клиентов с помощью спокойных вызовов, каждый из которых приводит к одному или нескольким запросам БД. Я обернул методы, которым нужен доступ к БД, и сохранил дескриптор соединения с БД в os.environ. Увидетьэтот ТАК вопрос / ответ в основном о том, как я это делаю.
Однако, как только несколько сотен клиентов подключаются к моему приложению и инициируют вызовы базы данных, я начинаю получать эти ошибки в журналах ошибок Google App Engine (и мое приложение, конечно, возвращает 500):
<code>could not connect: ApplicationError: 1033 Instance has too many concurrent requests: 100 Traceback (most recent call last): File "/base/python27_run </code>
Какие-нибудь советы от опытных пользователей Google App Engine и Google Cloud SQL? Заранее спасибо.
Вот код для декоратора, который я использую для методов, которые требуют подключения к БД:
<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>