Dav*_*vid 6 python memory-leaks google-cloud-datastore
我有一个 Web 应用程序,它使用 Google 的数据存储区,并且在收到足够多的请求后内存不足。
我已将其范围缩小到数据存储查询。下面提供了一个最小的 PoC,一个稍长的版本,包括内存测量在 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")
Run Code Online (Sandbox Code Playgroud)
分析流程 RSS 显示每次迭代大约增长 1 MiB。即使没有返回结果,也会发生这种情况。以下是我的 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
Run Code Online (Sandbox Code Playgroud)
但与此同时,Python 的mprof显示了一个平面图(运行如下mprof run python datastore_test.py):
问题
我调用 Datastore 的方式是否有问题,或者这可能是库的潜在问题?
环境是 Windows 10 上的 Python 3.7.4(也在 Docker 中的 Debian 上的 3.8 上进行了测试)google-cloud-datastore==1.11.0和grpcio==1.28.1.
编辑 1
澄清这不是典型的 Python 分配器行为,它从操作系统请求内存,但不会立即从内部领域/池中释放它。下面是来自 Kubernetes 的图表,我的受影响的应用程序在其中运行:
由此可见:
编辑 2
为了绝对确定 Python 的内存使用情况,我使用gc检查了垃圾收集器的状态。退出前,程序报告:
gc: done, 15966 unreachable, 0 uncollectable, 0.0156s elapsed
Run Code Online (Sandbox Code Playgroud)
我还在gc.collect()循环的每次迭代期间手动使用强制垃圾收集,这没有区别。
由于没有不可收集的对象,内存泄漏似乎不太可能来自使用 Python 的内部内存管理分配的对象。因此,外部 C 库更有可能泄漏内存。
可能相关
有一个开放的 grpc 问题,我不确定是否相关,但与我的问题有许多相似之处。
我已将内存泄漏范围缩小到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")
Run Code Online (Sandbox Code Playgroud)
这对于小脚本来说是有意义的,其中client对象可以创建一次并在请求之间安全地共享。
在许多其他应用程序中,安全地传递客户端对象更困难(或不可能)。我希望当客户端超出范围时库会释放内存,否则任何长时间运行的程序都可能会出现此问题。
编辑1
我已经将范围缩小到 grpc。GOOGLE_CLOUD_DISABLE_GRPC可以设置环境变量(为任意值)以禁用 grpc。
设置完成后,我在 Kubernetes 中的应用程序如下所示:
对 valgrind 的进一步调查表明,它可能与 grpc 中的 OpenSSL 使用有关,我在错误跟踪器的票证中记录了这一点。
| 归档时间: |
|
| 查看次数: |
994 次 |
| 最近记录: |