Skip to content

Commit

Permalink
Fix memory leak issue (#49)
Browse files Browse the repository at this point in the history
  • Loading branch information
daya0576 authored Dec 26, 2024
1 parent 757c9ef commit f31428e
Show file tree
Hide file tree
Showing 7 changed files with 191 additions and 137 deletions.
1 change: 0 additions & 1 deletion beaverhabits/api.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import logging
from datetime import datetime

from fastapi import APIRouter, Depends, FastAPI, HTTPException
Expand Down
1 change: 1 addition & 0 deletions beaverhabits/configs.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class StorageType(Enum):

class Settings(BaseSettings):
ENV: str = "dev"
DEBUG: bool = False
SENTRY_DSN: str = ""

# UI config
Expand Down
43 changes: 42 additions & 1 deletion beaverhabits/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import sentry_sdk
from fastapi import FastAPI
from nicegui import ui

from beaverhabits.api import init_api_routes
from beaverhabits.app import crud
Expand Down Expand Up @@ -33,7 +34,7 @@ async def lifespan(_: FastAPI):
def startup():
loop = asyncio.get_running_loop()
loop.set_debug(True)
loop.slow_callback_duration = 0.05
loop.slow_callback_duration = 0.01


@app.get("/health")
Expand All @@ -58,6 +59,46 @@ async def user_count():
sentry_sdk.init(settings.SENTRY_DSN)


if settings.DEBUG:
import gc
import os
from collections import Counter

import psutil
from psutil._common import bytes2human

class MemoryMonitor:

def __init__(self) -> None:
self.last_mem: int = 0
self.obj_count: dict[str, int] = {}

def print_stats(self) -> None:
gc.collect()
memory = psutil.Process(os.getpid()).memory_info().rss
growth = memory - self.last_mem
print(bytes2human(memory), end=" ")
print(
bytes2human(growth, r"%(value)+.1f%(symbol)s") if growth else "",
end=" ",
)
counter: Counter = Counter()
for obj in gc.get_objects():
counter[type(obj).__name__] += 1
for cls, count in counter.items():
prev_count = self.obj_count.get(cls, 0)
if count != prev_count:
print(f"{cls}={count} ({count - prev_count:+})", end=" ")
self.obj_count[cls] = count
self.obj_count = {
cls: count for cls, count in self.obj_count.items() if count > 0
}
print()
self.last_mem = memory

monitor = MemoryMonitor()
ui.timer(5.0, monitor.print_stats)

if __name__ == "__main__":
print(
'Please start the app with the "uvicorn"'
Expand Down
2 changes: 0 additions & 2 deletions beaverhabits/storage/dict.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
import logging
from dataclasses import dataclass, field

import pytz

from beaverhabits.storage.storage import CheckedRecord, Habit, HabitList, HabitStatus
from beaverhabits.utils import generate_short_hash

Expand Down
9 changes: 5 additions & 4 deletions beaverhabits/storage/user_file.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from pathlib import Path
from typing import Optional

from fastapi_users_db_sqlalchemy import UUID_ID
from nicegui.storage import PersistentDict

from beaverhabits.app.db import User
Expand All @@ -13,17 +14,17 @@

class UserDiskStorage(UserStorage[DictHabitList]):
def __init__(self):
self.user: dict[User, PersistentDict] = {}
self.user: dict[UUID_ID, PersistentDict] = {}

def _get_persistent_dict(self, user: User) -> PersistentDict:
if user in self.user:
return self.user[user]
if user.id in self.user:
return self.user[user.id]

path = Path(f"{USER_DATA_FOLDER}/{str(user.email)}.json")
d = PersistentDict(path, encoding="utf-8")

# Cache the persistent dict
self.user[user] = d
self.user[user.id] = d

return d

Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ dev = [
"beautifulsoup4<5.0.0,>=4.12.3",
"pytest<9.0.0,>=8.1.1",
"pip>=24.3.1",
"psutil>=6.1.1",
]

[project.scripts]
Expand Down
Loading

0 comments on commit f31428e

Please sign in to comment.