PyMySQL в Flask/Apache иногда возвращает пустой результат

У меня есть приложение Flask, работающее в Apache, которое использует PyMySQL. Приложение предоставляет ряд команд REST. Он работает под Python 3.

Без предоставления всего исходного кода программа имеет следующую структуру:

#!flask/bin/python
import json
import pymysql
from flask import *

# Used to hopefully share the connection if the process isn't restarted
mysql_connection = None   

# Gets the mysql_connection, or opens it
GetStoreCnx():
    global mysql_connection
    if (mysql_connection != None):
        store_connection_string = ""
        # Get the connection string from the config file
        with open('config/storedb.json', 'r') as f:
            store_connection_string = json.load(f)
        mysql_connection = pymysql.connect(**store_connection_string)
    return mysql_connection;


class Server(Flask):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

# Return results via REST
@app.route('/results1', methods=['GET'])
def get_result1():
    cnx = GetStoreCnx();
    cursor = cnx.cursor();
    query = """
        SELECT 
            result_name,
            successful
        FROM
            results
        """
    cursor.execute(query)
    cnx.commit()
    result = cursor.fetchall()
    return json.dumps(result)

# Run server, if needed
if __name__ == '__main__':
    app.run(host='0.0.0.0', debug=True)

Есть еще несколько вызовов REST, но все они, по сути, делают одно и то же (например, получают соединение, создают курсор, запускают базовый запрос на выборку, который иногда имеет более двух полей, выполняют запрос, получают результат и вернуть его как объект JSON). Коммиты там должны быть ненужными, но, похоже, это текущая проблема с PyMySQL, которая приводила к получению старых данных.

Проблема в том, что эти вызовы REST иногда возвращают пустые наборы JSON (т.е. []). Дальнейшее исследование показало, что вызов execute иногда возвращает совершенно пустой результат, но не генерирует исключение. Это происходит регулярно - но не постоянно. Некоторые вызовы успешно возвращают значения. Когда я пытаюсь продолжить вызов, пока он не вернет результат (например:

while(cursor.execute(query) < 1):
    pass

) процесс входит в бесконечный цикл, в конечном итоге (быстро) не позволяя apache обслуживать новые запросы.

Сервер (на данный момент) обслуживает только около 5 вызовов в секунду. Проблема не появляется, если я использую сервер разработки.

Можно ли как-то предотвратить эту ошибку? Это ошибка в PyMySQL? Что-то, что я делаю, препятствует правильному подключению к MySQL?


person lochok    schedule 09.12.2015    source источник


Ответы (1)


Вы создаете одно глобальное соединение mysql, которое используется вашим приложением, при этом pymysql объявляет threadsafety из 1, что согласно спецификация dbapi2 означает:

1   Threads may share the module, but not connections. 

Поскольку одновременные запросы в flask будут обслуживаться разными потоками, вы не должны совместно использовать соединение. Причина, по которой у вас не возникает проблем при использовании сервера разработки, заключается в том, что он работает в однопоточном режиме.

Чтобы избежать этого, вы можете:

  • создайте новое соединение для каждого потока, сохраните его как локальный поток для дальнейшего использования
  • создавать новое соединение для каждого запроса, сохранять его в flask.g для дальнейшего использования

Для этого ваша функция GetStoreCnx может быть изменена следующим образом:

import threading
thread_local = threading.local()

def GetStoreCnx():        
    if not hasattr(thread_local, 'mysql_connection'):
        store_connection_string = ""
        # Get the connection string from the config file
        with open('config/storedb.json', 'r') as f:
            store_connection_string = json.load(f)
        mysql_connection = pymysql.connect(**store_connection_string)
        thread_local.mysql_connection = mysql_connection
    return thread_local.mysql_connection;

SQLAlchemy делает нечто подобное со своим scoped_session(). Это также должно работать с flask.g вместо thread_local для одного соединения на запрос.

person mata    schedule 19.09.2016