Flask Session (файловая система) UnPicklingError для объектов сеанса, есть ли ограничение по размеру или я получаю повреждения? - PullRequest
0 голосов
/ 16 апреля 2019

Python2.7 / Centos7.5 / Apache2.4.6 + mod_wsgi

Я использую flask / jinja для создания некоторых интерактивных HTML-форм и сохранения данных в файл сеанса между изменениями (flask_session / type = filesystem).Это случайно разбивается с ошибкой Unpickling во время функции _prune в Werkzeug contrib / cache.py, и я не знаю, почему.Удаление файлов сеанса исправило бы проблему, пока это не всплыло снова.С тех пор я смог воспроизвести ошибку, заставив файл сеанса увеличиваться в размере (делая форму длиннее), поэтому я подозреваю, что размер связан, но это не совсем объясняет, почему они произошли в первую очередь, Существуют ли ограничения по размеру файлов сеансов / серверных файлов на стороне сервера, с которыми я работаю? Это не вполне предсказуемо, я просто знаю, что когда я начну перегружать вещи, это в конечном итоге произойдет.AFAIK Ничто не отправляется на стороне клиента, за исключением рендеринга jinja html.

Я загружаю OrderedDict приличного размера в качестве элемента сеанса:

session['saved'] = OrderedDict(items_list)

Глубина диктататолько 3 уровня, с общим количеством ключей ~ 300, когда существует риск сбоя.

Я попытался изменить порог элементов сеанса, изменить постоянство файла сеанса, обновить werkzeug, и у меня естьОбходной путь путем принудительного удаления файла сеанса в UnpicklingError (который, кажется, вообще не влияет на работу на стороне клиента).Но это ручное редактирование lib-файла werkzeug, и оно определенно не является долгосрочным решением.

Traceback (most recent call last):
File "/usr/lib64/python2.7/site-packages/flask/app.py", line 2292, in wsgi_app
  response = self.full_dispatch_request()
File "/usr/lib64/python2.7/site-packages/flask/app.py", line 1816, in full_dispatch_request
  return self.finalize_request(rv)
File "/usr/lib64/python2.7/site-packages/flask/app.py", line 1833, in finalize_request
  response = self.process_response(response)
File "/usr/lib64/python2.7/site-packages/flask/app.py", line 2114, in process_response
  self.session_interface.save_session(self, ctx.session, response)
File "/usr/lib64/python2.7/site-packages/flask_session/sessions.py", line 355, in save_session
  total_seconds(app.permanent_session_lifetime))
File "/usr/lib/python2.7/site-packages/werkzeug/contrib/cache.py", line 815, in set
  self._prune()
File "/usr/lib/python2.7/site-packages/werkzeug/contrib/cache.py", line 764, in _prune
  expires = pickle.load(f)
UnpicklingError: invalid load key, '*'.

Соответствующий код Python (обрезается и модифицируется для удобства чтения):

from flask import Flask, session, request, render_template
from flask_session import Session
from collections import OrderedDict
app = Flask(__name__)
SESSION_TYPE = app.config['SESSION_TYPE'] = 'filesystem'
SESSION_FILE_DIR = app.config['SESSION_FILE_DIR'] = os.path.join(local_dir,'flask_session')
SESSION_FILE_THRESHOLD = app.config['SESSION_FILE_THRESHOLD'] = 100
Session.init_app(app)

@app.route('/', defaults={'path': ''}, methods=['POST','GET'])
@app.route('/<path:path>', methods=['POST','GET'])
def configuration(path):
    #Do Some Stuff
    session['saved'] = OrderedDict(items_list)
    return render_template('configuration.html', session=session)

Соответствуетjinja

% for element, details in session.saved.items()
    <tr id="{{element}}" name="{{details['type']}}">
            <td>
        <select id="{{element}}" name="{{element}}">
            % for item, desc in details["items"]
                <option value="{{item}}">{{desc}}</option>
            % endfor
        </select>
        </td>
        </tr>

Функция _prune из werkzeug / contrib / cache.py для удобства

 def _prune(self):
        if self._threshold == 0 or not self._file_count > self._threshold:
            return

        entries = self._list_dir()
        now = time()
        for idx, fname in enumerate(entries):
            try:
                remove = False
                with open(fname, "rb") as f:
                    expires = pickle.load(f)
                remove = (expires != 0 and expires <= now) or idx % 3 == 0

                if remove:
                    os.remove(fname)
            except (IOError, OSError):
                pass
# Add Exception to delete file with pickle errors
            except pickle.UnpicklingError:
                os.remove(fname)
        self._update_count(value=len(self._list_dir()))

Я не считаю это уместным, учитывая, что все должно быть на стороне сервера, но работаетте же самые сценарии в среде Windows с сервером разработки колб не представляют ошибки травления.Вместо этого я получаю ошибку сокета в self.flush () - установленное соединение было прервано программным обеспечением на вашем хост-компьютере.Там тоже нет аварии, она просто продолжается.Я подозреваю, что это просто обработка ошибок, но подумал, что стоит упомянуть.

1 Ответ

0 голосов
/ 18 апреля 2019

Оказывается, в каталоге flask_session был другой файл, который werkzeug / contrib / cache пытался загрузить и проверить, нужно ли его обрезать:

.gitignore

* Facepalm *

Ответ здесь заключается в том, что Нет, это не ограничение по размеру, и нет, там нет коррупции. библиотека пыталась удалить файлы, основываясь исключительно на содержимом каталога.

...