Memory leak in python/flask?

Apropo de măreţul garbage colector, cum rezolv problema unui memory leak într-un limbaj ca python? :slight_smile:

Am un app extrem de simplu in flask, care salveaza un log in db şi am observat că e alocată pentru el din ce în ce mai multă memorie (deja sunt 1.7GB), pe zi ce trece, deci cel mai probabil nu eliberează RAM-ul. Nu-mi dau seama ce anume provoacă asta.

from flask import Flask, request
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.sql import func

app = Flask(__name__)

app.config["SQLALCHEMY_DATABASE_URI"] = "mysql://email:xxxxxxxxxx@localhost/email"
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
app.config["SQLALCHEMY_ECHO"] = True

db = SQLAlchemy(app)


class Log(db.Model):
    __tablename__ = "inout_log"
    id = db.Column(db.Integer, primary_key=True)
    added_date = db.Column(db.DateTime, server_default=func.now())
    ip = db.Column(db.String(20))
    ip_rev = db.Column(db.String(100))
    country = db.Column(db.String(100))
    helo = db.Column(db.String(100))
    auth_user = db.Column(db.String(100))
    env_from = db.Column(db.String(100))
    env_to = db.Column(db.String(100))
    header_from = db.Column(db.String(100))
    header_to = db.Column(db.String(100))
    header_cc = db.Column(db.String(255))
    header_replyto = db.Column(db.String(100))
    subject = db.Column(db.String(255))
    direction = db.Column(db.Integer) # 1 - in, 2 - out
    status = db.Column(db.Integer) # 0 - rejected, 1 - accepted
    log = db.Column(db.String(255))

    @classmethod
    def save(cls):
        model_obj = cls()

        # get model's fields
        valid_fields = [ field_name for field_name in dir(cls) if field_name != "id" and type(getattr(cls, field_name)).__name__ == "InstrumentedAttribute" ]

        # load values from POST data into model's fields
        for field in valid_fields:
            value = request.form.get(field)
            if value:
                setattr(model_obj, field, value)

        db.session.add(model_obj)
        db.session.commit()


@app.route("/log", methods = ["POST"])
def add():
    Log.save()
    return "OK"

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=60005, debug=True)
1 Like

Incearca si fara Debug mode.
Altii s-au mai plans aici: Memory leak running Flask in debug mode · Issue #3540 · pallets/flask · GitHub

5 Likes

Mersi pentru idee, sună plauzibil. Am dezactivat acum debug-ul, să vedem până mâine ce se întâmplă.

O alta idee.

In Log.save adaugi date in obiectul sesiune. Poate le ține inca în memorie in vreun cache chiar daca ai apelat commit.

Că să vezi că e memory leak si nu doar niste coaching agresiv, poate sa testezi într-un container cu memorie limitata și să vezi dacă da OOM sau pur și simplu incepe sa mai curețe din cache.

Also, poate citirea in modul asta din request-ul global păstrează referințe in sesiune la obiectele ce reprezintă request-ul și aduga și acolo niste presiune. Arată funky de tot citirea din globală aia

2 Likes

Un workaround la îndemână e reciclarea periodica a proceselor Python.

De ex. in conf uwsgi la fiecare 5000 de request-uri.

Yep, aplicația mea a rămas stabilă la 38 MB de memorie ocupată. Mulțumesc tuturor pentru idei și de reținut că nu scapi de memleaks nici in Py :slight_smile:

1 Like