Fix memory leak in Index.build_graph()

`@memoize()` memory lives as long as decorated function lives, which is
permanent in this case. Since `self` is referenced as a memory key no
`Index` will ever be freed.

Changed to use `@cached_property` whose cache lives as long as the
parent instance.
This commit is contained in:
Alexander Schepanovski
2022-01-03 13:41:10 +02:00
committed by Ruslan Kuprieiev
parent 0e0e1725e6
commit 9ee428ef17
3 changed files with 11 additions and 14 deletions
+7 -11
View File
@@ -11,7 +11,7 @@ from typing import (
Set,
)
from funcy import cached_property, memoize, nullcontext
from funcy import cached_property, nullcontext
from dvc.utils import dict_md5
@@ -146,9 +146,11 @@ class Index:
return build_outs_trie(self.stages)
@property
@cached_property
def graph(self) -> "DiGraph":
return self.build_graph()
from dvc.repo.graph import build_graph
return build_graph(self.stages, self.outs_trie)
@cached_property
def outs_graph(self) -> "DiGraph":
@@ -230,15 +232,9 @@ class Index:
stages.remove(stage)
return stages
@memoize
def build_graph(self) -> "DiGraph":
from dvc.repo.graph import build_graph
return build_graph(self.stages, self.outs_trie)
def check_graph(self) -> None:
if not getattr(self.repo, "_skip_graph_checks", False):
self.build_graph()
self.graph # pylint: disable=pointless-statement
def dumpd(self) -> Dict[str, Dict]:
def dump(stage: "Stage"):
@@ -275,7 +271,7 @@ if __name__ == "__main__":
# pylint: disable=pointless-statement
print("no of stages", len(index.stages))
with log_durations(print, "building graph"):
index.build_graph()
index.graph # pylint: disable=pointless-statement
with log_durations(print, "calculating hash"):
print(index.identifier)
with log_durations(print, "updating"):
+1 -2
View File
@@ -24,7 +24,6 @@ def test_index(tmp_dir, scm, dvc, run_copy):
assert index.outs_graph
assert index.graph
assert index.build_graph()
assert isinstance(index.outs_trie, Trie)
assert index.identifier
index.check_graph()
@@ -229,7 +228,7 @@ def test_unique_identifier(tmp_dir, dvc, scm, run_copy):
def test_skip_graph_checks(dvc, mocker):
# See https://github.com/iterative/dvc/issues/2671 for more info
mock_build_graph = mocker.spy(Index, "build_graph")
mock_build_graph = mocker.spy(Index.graph, "fget")
# sanity check
Index(dvc).check_graph()
+3 -1
View File
@@ -77,7 +77,9 @@ def test_locked(mocker):
def test_skip_graph_checks(tmp_dir, dvc, mocker, run_copy):
# See https://github.com/iterative/dvc/issues/2671 for more info
mock_build_graph = mocker.patch("dvc.repo.index.Index.build_graph")
from dvc.repo.index import Index
mock_build_graph = mocker.spy(Index.graph, "fget")
# sanity check
tmp_dir.gen("foo", "foo text")