Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add local lock to ensure 2 tasks aren't making changes to the local directory at the same time (updated) #5282

Draft
wants to merge 2 commits into
base: stable
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions backend/infrahub/git/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from pydantic import BaseModel, ConfigDict, Field
from pydantic import ValidationError as PydanticValidationError

from infrahub import lock
from infrahub.core.branch import Branch
from infrahub.core.constants import InfrahubKind
from infrahub.core.registry import registry
Expand All @@ -28,6 +29,7 @@
from infrahub.git.constants import BRANCHES_DIRECTORY_NAME, COMMITS_DIRECTORY_NAME, TEMPORARY_DIRECTORY_NAME
from infrahub.git.directory import get_repositories_directory, initialize_repositories_directory
from infrahub.git.worktree import Worktree
from infrahub.lock import LOCAL_REPO_LOCK
from infrahub.log import get_logger
from infrahub.services import InfrahubServices # noqa: TCH001

Expand Down Expand Up @@ -306,13 +308,24 @@ def validate_local_directories(self) -> bool:

async def create_locally(
self, checkout_ref: str | None = None, infrahub_branch_name: str | None = None, update_commit_value: bool = True
) -> bool:
async with lock.registry.get(name=LOCAL_REPO_LOCK, namespace=self.name, local=True):
return await self._create_locally(
checkout_ref=checkout_ref,
infrahub_branch_name=infrahub_branch_name,
update_commit_value=update_commit_value,
)

async def _create_locally(
self, checkout_ref: str | None = None, infrahub_branch_name: str | None = None, update_commit_value: bool = True
) -> bool:
"""Ensure the required directory already exist in the filesystem or create them if needed.

Returns
True if the directory has been created,
False if the directory was already present.
"""

initialize_repositories_directory()

if not self.location:
Expand Down Expand Up @@ -653,6 +666,11 @@ async def validate_remote_branch(self, branch_name: str) -> bool:

async def pull(self, branch_name: str) -> Union[bool, str]:
"""Pull the latest update from the remote repository on a given branch."""
async with lock.registry.get(name=LOCAL_REPO_LOCK, namespace=self.name, local=True):
return await self._pull_unsafe(branch_name=branch_name)

async def _pull_unsafe(self, branch_name: str) -> Union[bool, str]:
"""Pull the latest update from the remote repository on a given branch."""

if not self.has_origin:
return False
Expand Down
5 changes: 3 additions & 2 deletions backend/infrahub/git/integrator.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,9 @@ async def init(cls, commit: str | None = None, service: InfrahubServices | None
await self.ensure_location_is_defined()
await self.create_locally(infrahub_branch_name=self.infrahub_branch_name, update_commit_value=False)
service.log.info(f"Initialized the local directory for {self.name} because it was missing.")
if commit:
self.get_commit_worktree(commit=commit)

if commit:
self.get_commit_worktree(commit=commit)

service.log.debug(
f"Initiated the object on an existing directory for {self.name}",
Expand Down
10 changes: 10 additions & 0 deletions backend/infrahub/git/repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@
from infrahub_sdk.exceptions import GraphQLError
from pydantic import Field

from infrahub import lock
from infrahub.core.constants import InfrahubKind, RepositoryInternalStatus
from infrahub.exceptions import RepositoryError
from infrahub.git.integrator import InfrahubRepositoryIntegrator
from infrahub.lock import LOCAL_REPO_LOCK
from infrahub.log import get_logger
from infrahub.services import InfrahubServices

Expand Down Expand Up @@ -94,6 +96,10 @@ async def create_branch_in_git(
return True

async def sync(self, staging_branch: str | None = None) -> None:
async with lock.registry.get(name=LOCAL_REPO_LOCK, namespace=self.name, local=True):
return await self._sync_unsafe(staging_branch=staging_branch)

async def _sync_unsafe(self, staging_branch: str | None = None) -> None:
"""Synchronize the repository with its remote origin and with the database.

By default the sync will focus only on the branches pulled from origin that have some differences with the local one.
Expand Down Expand Up @@ -275,6 +281,10 @@ def get_commit_value(self, branch_name: str, remote: bool = False) -> str:
return str(commit)

async def sync_from_remote(self, commit: str | None = None) -> None:
async with lock.registry.get(name=LOCAL_REPO_LOCK, namespace=self.name, local=True):
return await self._sync_from_remote_unsafe(commit=commit)

async def _sync_from_remote_unsafe(self, commit: str | None = None) -> None:
if not commit:
commit = self.get_commit_value(branch_name=self.ref, remote=True)
local_branches = self.get_branches_from_local()
Expand Down
1 change: 1 addition & 0 deletions backend/infrahub/lock.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
)

LOCAL_SCHEMA_LOCK = "local.schema"
LOCAL_REPO_LOCK = "local.repository"
GLOBAL_INIT_LOCK = "global.init"
GLOBAL_SCHEMA_LOCK = "global.schema"
GLOBAL_GRAPH_LOCK = "global.graph"
Expand Down
Loading