Как мне исследовать утечку памяти при использовании библиотек Google Cloud Datastore Python? - PullRequest
3 голосов
/ 23 апреля 2020

У меня есть веб-приложение, которое использует хранилище данных Google, и после достаточного количества запросов у него не хватает памяти.

Я сузил это до запроса хранилища данных. Ниже приведено минимальное значение Po C, чуть более длинная версия , которая включает измерение памяти, на Github.

from google.cloud import datastore
from google.oauth2 import service_account

def test_datastore(entity_type: str) -> list:
    creds = service_account.Credentials.from_service_account_file("/path/to/creds")
    client = datastore.Client(credentials=creds, project="my-project")
    query = client.query(kind=entity_type, namespace="my-namespace")
    query.keys_only()
    for result in query.fetch(1):
        print(f"[+] Got a result: {result}")

for n in range(0,100):
    test_datastore("my-entity-type")

Профилирование процесса RSS показывает рост примерно на 1 МБ за итерацию. Это происходит, даже если результаты не возвращаются. Ниже приведен вывод из моего списка Github:

[+] Iteration 0, memory usage 38.9 MiB bytes
[+] Iteration 1, memory usage 45.9 MiB bytes
[+] Iteration 2, memory usage 46.8 MiB bytes
[+] Iteration 3, memory usage 47.6 MiB bytes
..
[+] Iteration 98, memory usage 136.3 MiB bytes
[+] Iteration 99, memory usage 137.1 MiB bytes

Но в то же время Python ' mprof показывает плоский график (работает как mprof run python datastore_test.py):

mprof output for 100 Datastore fetches

Вопрос

Я делаю что-то не так с тем, как я называю Datastore, или это вероятно основная проблема с библиотекой?

Среда Python 3.7.4 на Windows 10 (также протестирована на 3.8 на Debian в Docker) с google-cloud-datastore==1.11.0 и grpcio==1.28.1.

Edit 1

Пояснение: это не типичное Python поведение распределителя, когда оно запрашивает память у ОС, но не освобождает ее сразу от внутренних арен / пулов. Ниже приведен график из Kubernetes, где работает мое уязвимое приложение:

Memory usage graph from Kubernetes, showing a linear increase in memory usage as the process runs

Это показывает:

  • Линейный рост памяти до около 2 ГБ, где приложение фактически зависало из-за нехватки памяти (технически Kubernetes вытеснил модуль, но это не имеет значения).
  • То же веб-приложение, которое работает, но не взаимодействует ни с GCP Storage, ни с Datastore.
  • Взаимодействие только с добавленным хранилищем GCP (очень небольшой рост со временем, потенциально нормальный).
  • Взаимодействие только с добавленным хранилищем данных GCP (значительно больший прирост памяти, около 512 МБ в час). Запрос хранилища данных точно такой же, как и код Po C в этом сообщении.

Редактировать 2

Чтобы быть абсолютно уверенным в Python ' Используя память, я проверил состояние сборщика мусора, используя g c. Перед выходом программа сообщает:

gc: done, 15966 unreachable, 0 uncollectable, 0.0156s elapsed

Я также принудительно собирал мусор вручную, используя gc.collect() во время каждой итерации l oop, что не имело никакого значения.

Поскольку есть нет объектов, которые невозможно собрать, кажется маловероятным, что утечка памяти происходит от объектов, выделенных с помощью управления внутренней памятью Python. Поэтому более вероятно, что внешняя библиотека C вызывает утечку памяти.

Потенциально связана

Существует open grp c проблема я не уверен, что это связано, но у меня есть много общего с моей проблемой.

1 Ответ

3 голосов
/ 24 апреля 2020

Я сузил утечку памяти до создания объекта datastore.Client.

Для следующего проверочного кода использование памяти не увеличивается:

from google.cloud import datastore
from google.oauth2 import service_account

def test_datastore(client, entity_type: str) -> list:
    query = client.query(kind=entity_type, namespace="my-namespace")
    query.keys_only()
    for result in query.fetch(1):
        print(f"[+] Got a result: {result}")

creds = service_account.Credentials.from_service_account_file("/path/to/creds")
client = datastore.Client(credentials=creds, project="my-project")

for n in range(0,100):
    test_datastore(client, "my-entity-type")

Это имеет смысл для небольшого сценария, в котором объект client можно создать один раз и совместно использовать между запросами безопасно.

Во многих других приложениях труднее (или невозможно) безопасно обойти клиентский объект. Я ожидаю, что библиотека освободит память, когда клиент выйдет из области видимости, иначе эта проблема может возникнуть в любой долго работающей программе.

Изменить 1

У меня есть сузил это до GRP c. Переменная окружения GOOGLE_CLOUD_DISABLE_GRPC может быть установлена ​​(на любое значение) для отключения grp c.

После того, как это было установлено, мое приложение в Kubernetes выглядит так:

Memory usage graph from Kubernetes, showing a flat line (no memory increase) after grpc disabled

Дальнейшее исследование с использованием valgrind показывает, что оно, вероятно, связано с использованием OpenSSL в grp c, которое я задокументировал в этом билете на трекере ошибок.

...