mirror of
https://github.com/Stone-Red-Code/dvc.git
synced 2026-11-02 07:18:25 +01:00
repo: move state/index/data_index to site_cache_dir
We use all of these for optimization and should not depend on a potentially slow or broken
filesystem on which repository resides (could be HDD or CIFS/NFS/overlayfs/etc).
This PR means a few things:
* all odd filesystems that dvc repo might be on will work quickly out of the box
* when we start caching cloned repositories for `dvc get/import`, we will be
able to cache indexes for them as well (note that bare clones don't have a
worktree so there is no place for .dvc/tmp with the old approach).
* `index.dir` and `state.dir` are now obsolete.
* `dvc init` will cleanup site_cache_dir.
Related #5562
This commit is contained in:
@@ -273,12 +273,12 @@ SCHEMA = {
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
"state": {
|
"state": {
|
||||||
"dir": str,
|
"dir": str, # obsoleted
|
||||||
"row_limit": All(Coerce(int), Range(1)), # obsoleted
|
"row_limit": All(Coerce(int), Range(1)), # obsoleted
|
||||||
"row_cleanup_quota": All(Coerce(int), Range(0, 100)), # obsoleted
|
"row_cleanup_quota": All(Coerce(int), Range(0, 100)), # obsoleted
|
||||||
},
|
},
|
||||||
"index": {
|
"index": {
|
||||||
"dir": str,
|
"dir": str, # obsoleted
|
||||||
},
|
},
|
||||||
"machine": {
|
"machine": {
|
||||||
str: {
|
str: {
|
||||||
|
|||||||
+1
-1
@@ -75,7 +75,7 @@ class DataCloud:
|
|||||||
config["version_aware"] = True
|
config["version_aware"] = True
|
||||||
|
|
||||||
fs = cls(**config)
|
fs = cls(**config)
|
||||||
config["tmp_dir"] = self.repo.index_db_dir
|
config["tmp_dir"] = self.repo.site_cache_dir
|
||||||
if self.repo.data_index is not None:
|
if self.repo.data_index is not None:
|
||||||
index = self.repo.data_index.view(("remote", name))
|
index = self.repo.data_index.view(("remote", name))
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ def get_dvc_info():
|
|||||||
fs_root = get_fs_type(os.path.abspath(root_directory))
|
fs_root = get_fs_type(os.path.abspath(root_directory))
|
||||||
info.append(f"Workspace directory: {fs_root}")
|
info.append(f"Workspace directory: {fs_root}")
|
||||||
info.append(f"Repo: {_get_dvc_repo_info(repo)}")
|
info.append(f"Repo: {_get_dvc_repo_info(repo)}")
|
||||||
|
info.append(f"Repo.site_cache_dir: {repo.site_cache_dir}")
|
||||||
except NotDvcRepoError:
|
except NotDvcRepoError:
|
||||||
pass
|
pass
|
||||||
except SCMError:
|
except SCMError:
|
||||||
|
|||||||
+26
-43
@@ -134,38 +134,6 @@ class Repo:
|
|||||||
assert root_dir
|
assert root_dir
|
||||||
return root_dir, dvc_dir
|
return root_dir, dvc_dir
|
||||||
|
|
||||||
def _get_database_dir(self, db_name: str) -> Optional[str]:
|
|
||||||
from dvc.fs import localfs
|
|
||||||
|
|
||||||
# NOTE: by default, store SQLite-based remote indexes and state's
|
|
||||||
# `links` and `md5s` caches in the repository itself to avoid any
|
|
||||||
# possible state corruption in 'shared cache dir' scenario, but allow
|
|
||||||
# user to override this through config when, say, the repository is
|
|
||||||
# located on a mounted volume — see
|
|
||||||
# https://github.com/iterative/dvc/issues/4420
|
|
||||||
base_db_dir = self.config.get(db_name, {}).get("dir", None)
|
|
||||||
if not base_db_dir:
|
|
||||||
return self.tmp_dir
|
|
||||||
|
|
||||||
import hashlib
|
|
||||||
|
|
||||||
if self.local_dvc_dir:
|
|
||||||
fs: "FileSystem" = localfs
|
|
||||||
local_root = fs.path.parent(self.local_dvc_dir)
|
|
||||||
else:
|
|
||||||
fs = self.fs
|
|
||||||
local_root = self.root_dir
|
|
||||||
root_dir_hash = hashlib.sha224(local_root.encode("utf-8")).hexdigest()
|
|
||||||
|
|
||||||
db_dir = fs.path.join(
|
|
||||||
base_db_dir,
|
|
||||||
self.DVC_DIR,
|
|
||||||
f"{fs.path.name(local_root)}-{root_dir_hash[0:7]}",
|
|
||||||
)
|
|
||||||
|
|
||||||
fs.makedirs(db_dir, exist_ok=True)
|
|
||||||
return db_dir
|
|
||||||
|
|
||||||
def __init__( # noqa: PLR0915
|
def __init__( # noqa: PLR0915
|
||||||
self,
|
self,
|
||||||
root_dir: Optional[str] = None,
|
root_dir: Optional[str] = None,
|
||||||
@@ -241,8 +209,8 @@ class Repo:
|
|||||||
hardlink_lock=self.config["core"].get("hardlink_lock", False),
|
hardlink_lock=self.config["core"].get("hardlink_lock", False),
|
||||||
friendly=True,
|
friendly=True,
|
||||||
)
|
)
|
||||||
state_db_dir = self._get_database_dir("state")
|
os.makedirs(self.site_cache_dir, exist_ok=True)
|
||||||
self.state = State(self.root_dir, state_db_dir, self.dvcignore)
|
self.state = State(self.root_dir, self.site_cache_dir, self.dvcignore)
|
||||||
else:
|
else:
|
||||||
self.lock = LockNoop()
|
self.lock = LockNoop()
|
||||||
self.state = StateNoop()
|
self.state = StateNoop()
|
||||||
@@ -401,13 +369,9 @@ class Repo:
|
|||||||
from dvc_data.index import DataIndex
|
from dvc_data.index import DataIndex
|
||||||
|
|
||||||
if self._data_index is None:
|
if self._data_index is None:
|
||||||
if self.index_db_dir:
|
index_dir = os.path.join(self.site_cache_dir, "index", "data")
|
||||||
index_dir = os.path.join(self.index_db_dir, "index", "data")
|
os.makedirs(index_dir, exist_ok=True)
|
||||||
os.makedirs(index_dir, exist_ok=True)
|
self._data_index = DataIndex.open(os.path.join(index_dir, "db.db"))
|
||||||
|
|
||||||
self._data_index = DataIndex.open(os.path.join(index_dir, "db.db"))
|
|
||||||
else:
|
|
||||||
self._data_index = DataIndex()
|
|
||||||
|
|
||||||
return self._data_index
|
return self._data_index
|
||||||
|
|
||||||
@@ -590,8 +554,27 @@ class Repo:
|
|||||||
return DVCFileSystem(repo=self, subrepos=self.subrepos, **self._fs_conf)
|
return DVCFileSystem(repo=self, subrepos=self.subrepos, **self._fs_conf)
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def index_db_dir(self):
|
def site_cache_dir(self) -> str:
|
||||||
return self._get_database_dir("index")
|
import hashlib
|
||||||
|
|
||||||
|
import platformdirs
|
||||||
|
|
||||||
|
from dvc.fs import GitFileSystem
|
||||||
|
|
||||||
|
cache_dir = platformdirs.site_cache_dir("dvc", "iterative", opinion=True)
|
||||||
|
|
||||||
|
if isinstance(self.fs, GitFileSystem):
|
||||||
|
relparts = ()
|
||||||
|
if self.root_dir != "/":
|
||||||
|
# subrepo
|
||||||
|
relparts = self.fs.path.relparts(self.root_dir, "/")
|
||||||
|
root_dir = os.path.join(self.scm.root_dir, *relparts)
|
||||||
|
else:
|
||||||
|
root_dir = self.root_dir
|
||||||
|
|
||||||
|
repo_token = hashlib.md5(os.fsencode(root_dir)).hexdigest() # noqa: S324
|
||||||
|
|
||||||
|
return os.path.join(cache_dir, "repo", repo_token)
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def open_by_relpath(self, path, remote=None, mode="r", encoding=None):
|
def open_by_relpath(self, path, remote=None, mode="r", encoding=None):
|
||||||
|
|||||||
+6
-1
@@ -12,7 +12,7 @@ from dvc.utils.fs import remove
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def init(root_dir=os.curdir, no_scm=False, force=False, subdir=False):
|
def init(root_dir=os.curdir, no_scm=False, force=False, subdir=False): # noqa: C901
|
||||||
"""
|
"""
|
||||||
Creates an empty repo on the given directory -- basically a
|
Creates an empty repo on the given directory -- basically a
|
||||||
`.dvc` directory with subdirectories for configuration and cache.
|
`.dvc` directory with subdirectories for configuration and cache.
|
||||||
@@ -75,6 +75,11 @@ def init(root_dir=os.curdir, no_scm=False, force=False, subdir=False):
|
|||||||
|
|
||||||
proj = Repo(root_dir)
|
proj = Repo(root_dir)
|
||||||
|
|
||||||
|
if os.path.isdir(proj.site_cache_dir):
|
||||||
|
proj.close()
|
||||||
|
remove(proj.site_cache_dir)
|
||||||
|
proj = Repo(root_dir)
|
||||||
|
|
||||||
with proj.scm_context(autostage=True) as context:
|
with proj.scm_context(autostage=True) as context:
|
||||||
files = [
|
files = [
|
||||||
config.files["repo"],
|
config.files["repo"],
|
||||||
|
|||||||
+1
-1
@@ -48,7 +48,7 @@ dependencies = [
|
|||||||
"networkx>=2.5",
|
"networkx>=2.5",
|
||||||
"packaging>=19",
|
"packaging>=19",
|
||||||
"pathspec>=0.10.3",
|
"pathspec>=0.10.3",
|
||||||
"platformdirs<4,>=3",
|
"platformdirs<4,>=3.1.1",
|
||||||
"psutil>=5.8",
|
"psutil>=5.8",
|
||||||
"pydot>=1.2.4",
|
"pydot>=1.2.4",
|
||||||
"pygtrie>=2.3.2",
|
"pygtrie>=2.3.2",
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
import os
|
import os
|
||||||
import re
|
|
||||||
|
|
||||||
from dvc.repo import Repo
|
|
||||||
from dvc_data.hashfile.hash import file_md5
|
from dvc_data.hashfile.hash import file_md5
|
||||||
from dvc_data.hashfile.hash_info import HashInfo
|
from dvc_data.hashfile.hash_info import HashInfo
|
||||||
from dvc_data.hashfile.state import State
|
from dvc_data.hashfile.state import State
|
||||||
@@ -71,15 +69,3 @@ def test_get_unused_links(tmp_dir, dvc):
|
|||||||
dvc.fs,
|
dvc.fs,
|
||||||
)
|
)
|
||||||
) == {"bar"}
|
) == {"bar"}
|
||||||
|
|
||||||
|
|
||||||
def test_state_dir_config(make_tmp_dir, dvc):
|
|
||||||
assert dvc.state.tmp_dir == dvc.tmp_dir
|
|
||||||
|
|
||||||
index_dir = str(make_tmp_dir("tmp_index"))
|
|
||||||
repo = Repo(config={"state": {"dir": index_dir}})
|
|
||||||
assert os.path.dirname(repo.state.tmp_dir) == os.path.join(index_dir, ".dvc")
|
|
||||||
assert re.match(
|
|
||||||
r"^test_state_dir_config0-([0-9a-f]+)$",
|
|
||||||
os.path.basename(repo.state.tmp_dir),
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -1,11 +1,8 @@
|
|||||||
import os
|
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from dvc_gs import GSFileSystem
|
from dvc_gs import GSFileSystem
|
||||||
from dvc_s3 import S3FileSystem
|
from dvc_s3 import S3FileSystem
|
||||||
|
|
||||||
from dvc.fs import get_cloud_fs
|
from dvc.fs import get_cloud_fs
|
||||||
from dvc_data.hashfile.db import get_index
|
|
||||||
|
|
||||||
|
|
||||||
def test_remote_with_hash_jobs(dvc):
|
def test_remote_with_hash_jobs(dvc):
|
||||||
@@ -57,18 +54,3 @@ def test_makedirs_not_create_for_top_level_path(fs_cls, dvc, mocker):
|
|||||||
|
|
||||||
fs.makedirs(url)
|
fs.makedirs(url)
|
||||||
assert not mocked_client.called
|
assert not mocked_client.called
|
||||||
|
|
||||||
|
|
||||||
def test_remote_index_dir_config(make_tmp_dir, dvc):
|
|
||||||
index_dir = str(make_tmp_dir("tmp_index"))
|
|
||||||
with dvc.config.edit() as conf:
|
|
||||||
conf["index"]["dir"] = index_dir
|
|
||||||
conf["remote"]["s3"] = {"url": "s3://bucket/name"}
|
|
||||||
|
|
||||||
dvc.root_dir = "/usr/local/test_repo"
|
|
||||||
dvc.dvc_dir = "/usr/local/test_repo/.dvc"
|
|
||||||
dvc.__dict__.pop("local_dvc_dir")
|
|
||||||
|
|
||||||
assert os.path.dirname(
|
|
||||||
get_index(dvc.cloud.get_remote_odb(name="s3")).index_dir
|
|
||||||
) == os.path.join(index_dir, ".dvc", "test_repo-a473718", "index")
|
|
||||||
|
|||||||
Reference in New Issue
Block a user