Skip to content

Issue: memory leak when using rowTransaction #338

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
andre996 opened this issue Nov 5, 2024 · 2 comments
Open

Issue: memory leak when using rowTransaction #338

andre996 opened this issue Nov 5, 2024 · 2 comments
Labels
bug something broken

Comments

@andre996
Copy link

andre996 commented Nov 5, 2024

Description:

when using rowTransactions. Every time a transaction is performed, a memory leak occurs. This is due to new row references being created while old ones aren’t being removed, leading to an increase in Shallow Size and Retained Size.

Image

Sample code:

import dash_ag_grid as dag
from dash import Dash, html, Input, Output, State, ctx, no_update, callback

app = Dash(__name__)

rowData = [
    {"id": "Toyota_0", "make": "Toyota", "model": "Celica", "price": 35000},
    {"id": "Ford_0", "make": "Ford", "model": "Mondeo", "price": 32000},
    {"id": "Porsche_0", "make": "Porsche", "model": "Boxster", "price": 72000},
]

columnDefs = [
    {"field": "id", "checkboxSelection": True, "headerCheckboxSelection": True},
    {"field": "make"},
    {"field": "model"},
    {"field": "price", "cellRenderer": "agAnimateShowChangeCellRenderer", },
]

columnDefs_leet = [
    {"field": "id",  'headerName':  "DSFDS","checkboxSelection": True, "headerCheckboxSelection": True},
    {"field": "make", 'headerName': "DFA VXC" ,},
    {"field": "model", 'headerName': "DSS Z",},
    {"field": "price", 'headerName': "SDEAW", "cellRenderer": "agAnimateShowChangeCellRenderer", },
]


app.layout = html.Div(
    [
        html.Button("Update column def", id="btn-header-names-toggle"),
        html.Button("Add Rows", id="btn-client-side-transaction-add"),
        html.Button("Add Rows at index 2", id="btn-client-side-transaction-add-index-2"),
        html.Button("Update selected", id="btn-client-side-transaction-update"),
        html.Button("Remove Selected", id="btn-client-side-transaction-remove"),
        html.Button("Clear", id="btn-client-side-transaction-clear"),
        html.Button("Start Over", id="btn-client-side-transaction-start"),
        dag.AgGrid(
            id="client-side-transaction",
            rowData=rowData,
            columnDefs=columnDefs,
            columnSize="sizeToFit",
            dashGridOptions={"rowSelection": "multiple"},
            getRowId="params.data.id"
        ),
    ],
)

@callback(
    Output("client-side-transaction", "columnDefs"),
    Input("btn-header-names-toggle", "n_clicks"),
     State("client-side-transaction", "columnDefs"),
    prevent_initial_call=True
)
def toggle_cols(n, column_defs):
    if n % 2 == 0:
        return columnDefs
    return columnDefs_leet
    # print('Changing column def')
    # for col in column_defs:
    #     col["valueFormatter"] = {"function": "'* ' + params.value + ' *'"}
    # print(column_defs)
    # return columnDefs

@callback(
    Output("client-side-transaction", "rowData"),
    Input("btn-client-side-transaction-clear", "n_clicks"),
    Input("btn-client-side-transaction-start", "n_clicks"),
)
def update_rowdata(*_):
    return [] if ctx.triggered_id == "btn-client-side-transaction-clear" else rowData


@callback(
    Output("client-side-transaction", "rowTransaction"),
    Input("btn-client-side-transaction-add", "n_clicks"),
    Input("btn-client-side-transaction-add-index-2", "n_clicks"),
    Input("btn-client-side-transaction-update", "n_clicks"),
    Input("btn-client-side-transaction-remove", "n_clicks"),
    State("client-side-transaction", "selectedRows"),
    prevent_initial_call=True,
)
def update_transaction(n1, n2, n3, n4, selection):
    if ctx.triggered_id in ["btn-client-side-transaction-add", "btn-client-side-transaction-add-index-2"]:
        newRows = [
            {
                "id": row["make"] + '_' + str((n1 or 0) + (n2 or 0)),
                "make": row["make"], "model": row["model"], "price": row["price"]
            } for row in rowData
        ]

        return (
            {"add": newRows}
            if ctx.triggered_id == "btn-client-side-transaction-add"
            else {"add": newRows, 'addIndex': 2}
        )

    if selection:
        if ctx.triggered_id == "btn-client-side-transaction-update":
            for row in selection:
                row["price"] = row["price"] + n3
            return {"update": selection}

        if ctx.triggered_id == "btn-client-side-transaction-remove":
            return {"remove": selection}

    # If no rows selected, no grid update
    return no_update


if __name__ == "__main__":
    app.run(debug=True)

Ref: https://dash.plotly.com/dash-ag-grid/client-side#transaction-updates

FYI: There is also an increase of memory use with rowData but it is smaller

@gvwilson gvwilson added the bug something broken label Nov 11, 2024
@BSd3v
Copy link
Collaborator

BSd3v commented Feb 3, 2025

Hello @andre996,

Is there another way to measure this rather than having to watch the network tab?

@andre996
Copy link
Author

Hey @BSd3v, after some research, I found memlab to check for memory leaks. It's a tool developed by Facebook that is used to find memory leaks in JS code.

Image

Memlab takes a snapshot of the app when it loads after it interacts, and when it returns to the main stage. In this case, the interaction was to add 12 rows in batches of 3. As you can see on the report the memory usage passed from 17MB to 18.4MB, this change was caused by two types of leaks, the first one I think is related to adding the 12 rows to the ag-grid, and the second is caused by the number of time the add 3 rows button was clicked

Sample app and code used to generate the report
data_leaking.zip

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug something broken
Projects
None yet
Development

No branches or pull requests

3 participants