Flask Session(文件系统)会话对象上的UnPicklingError,是否存在大小限制或者是否存在损坏?

问题描述 投票:0回答:1

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

我正在使用flask / jinja生成一些交互式html表单,并在更改之间将数据保存到会话文件(flask_session / type = filesystem)。它在werkzeug的contrib / cache.py的_prune函数中随机破坏了一个Unpickling错误,我为什么不知所措。删除会话文件将解决问题,直到它再次出现。我已经能够通过强制会话文件的大小增加(使表单更长)来重现错误,所以我怀疑大小是相关的,但它并不完全解释为什么它们发生在第一位。我遇到了服务器端会话文件/泡菜的一些大小限制吗?它不是完全可以预测的,我只知道当我开始重载时它最终会发生。除了jinja呈现的html之外,AFAIK没有被发送到客户端。

我加载一个体面大小的OrderedDict作为会话项:

session['saved'] = OrderedDict(items_list)

dict的深度只有3个级别,当它有崩溃的风险时总共有大约300个键。

我已经尝试更改会话项阈值,更改会话文件的持久性,更新werkzeug,并通过强制删除UnpicklingError上的会话文件来解决方法(这似乎不会影响客户端的体验)所有)。但这是对werkzeug lib文件的手动编辑,绝对不是一个长期的解决方案。

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, '*'.

Relevant python code (trimmed and modified for readability):

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)

Relevant 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 function from werkzeug/contrib/cache.py for convenience

 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()中遇到套接字错误 - 已建立的连接被主机中的软件中止。也没有崩溃,它只是继续。我怀疑这只是错误处理,但认为值得一提。

python flask pickle werkzeug flask-session
1个回答
0
投票

事实证明,flask_session目录中还有另一个文件,werkzeug / contrib / cache正在尝试加载并检查是否需要修剪:

.gitignore

* facepalm *

这里的答案是,不,这不是规模限制,也没有腐败。该库试图纯粹基于目录内容修剪文件。

© www.soinside.com 2019 - 2024. All rights reserved.