From ed34dc973e12768e5d640dfa616725ad69104f91 Mon Sep 17 00:00:00 2001 From: gaetanbrl Date: Wed, 18 Feb 2026 14:23:13 +0100 Subject: [PATCH] try to fix worker error --- srv/python/mviewerstudio_backend/route.py | 7 +- .../mviewerstudio_backend/utils/git_utils.py | 107 +++++++++++------- 2 files changed, 72 insertions(+), 42 deletions(-) diff --git a/srv/python/mviewerstudio_backend/route.py b/srv/python/mviewerstudio_backend/route.py index b87366e6..9644ccda 100644 --- a/srv/python/mviewerstudio_backend/route.py +++ b/srv/python/mviewerstudio_backend/route.py @@ -16,7 +16,7 @@ from flask.blueprints import BlueprintSetupState from urllib.parse import urlparse import requests -from .utils.git_utils import Git_manager +from .utils.git_utils import Git_manager, checkout from datetime import datetime from werkzeug.exceptions import BadRequest, MethodNotAllowed, Conflict @@ -439,7 +439,10 @@ def preview_app_version(id, version) -> Response: ) replace_templates_url(path_preview_file, relative_publish_dir) # restor branch - git.repo.git.checkout("master") + try: + checkout(git.repo, "master") + except Exception: + git.repo.git.checkout("master") preview_url = path.join(config["publisher"], preview_file) diff --git a/srv/python/mviewerstudio_backend/utils/git_utils.py b/srv/python/mviewerstudio_backend/utils/git_utils.py index b094d206..f83333ec 100644 --- a/srv/python/mviewerstudio_backend/utils/git_utils.py +++ b/srv/python/mviewerstudio_backend/utils/git_utils.py @@ -1,19 +1,30 @@ import git -from os import path, remove +import logging +import fcntl +from contextlib import contextmanager from datetime import datetime +from os import path + from .login_utils import current_user -import logging -from time import sleep logger = logging.getLogger(__name__) -def unlock_repo(repo): +@contextmanager +def repo_write_lock(repo): """ - Force remove git lock file + Acquire an inter-process lock for write operations on this repository. + This serializes git write commands across Gunicorn workers. """ - lock_file = path.join(repo.working_dir, ".git", "index.lock") - remove(lock_file) + lock_path = path.join(repo.working_dir, ".git", "mviewerstudio.lock") + lock_file = open(lock_path, "w") + fcntl.flock(lock_file.fileno(), fcntl.LOCK_EX) + + try: + yield + finally: + fcntl.flock(lock_file.fileno(), fcntl.LOCK_UN) + lock_file.close() def init_repo(workspace): @@ -64,18 +75,18 @@ def checkout(repo, target, hard=False): logger.debug(repo.working_dir) repo_dir = repo.working_dir - lock_file = path.join(repo.working_dir, ".git", "index.lock") - if path.exists(lock_file): - logger.error(f"GIT : REPO LOCKED {repo_dir}") - sleep(5) + with repo_write_lock(repo): + lock_file = path.join(repo.working_dir, ".git", "index.lock") + if path.exists(lock_file): + logger.debug(f"GIT : INDEX LOCK PRESENT {repo_dir}, waiting for lock release") - if hard: - logger.debug(f"GIT : RESET HARD {target}") - repo.git.reset("--hard", target) - else: - logger.debug(f"GIT : CHECKOUT {target}") - repo.git.checkout(target) + if hard: + logger.debug(f"GIT : RESET HARD {target}") + repo.git.reset("--hard", target) + else: + logger.debug(f"GIT : CHECKOUT {target}") + repo.git.checkout(target) class Git_manager: @@ -98,7 +109,8 @@ def create_version(self, msg=""): :param msg: str tag message """ logger.debug("GIT : CREATE TAG") - self.repo.create_tag(datetime.now().strftime("%Y-%m-%d-%H-%M-%S"), message=msg) + with repo_write_lock(self.repo): + self.repo.create_tag(datetime.now().strftime("%Y-%m-%d-%H-%M-%S"), message=msg) def delete_version(self, version_name): """ @@ -107,13 +119,17 @@ def delete_version(self, version_name): """ # delete tag logger.debug("GIT : DELETE VERSION") - tag = self.repo.tags[version_name] - if tag: - # delete version from tag object - logger.debug("GIT : DELETE TAG") - self.repo.delete_tag(tag) - if len(self.repo.tags) == 1: - self.switch_version(self.repo.tags[0].name, True) + switch_to_tag = None + with repo_write_lock(self.repo): + tag = self.repo.tags[version_name] + if tag: + # delete version from tag object + logger.debug("GIT : DELETE TAG") + self.repo.delete_tag(tag) + if len(self.repo.tags) == 1: + switch_to_tag = self.repo.tags[0].name + if switch_to_tag: + self.switch_version(switch_to_tag, True) def clean_branch(self): """ @@ -131,9 +147,10 @@ def delete_versions(self): """ # delete tag logger.debug("GIT : DELETE TAG (USER DELETE VERSION)") - for tag in self.repo.tags: - if int(tag.name) > 1: - self.repo.delete_tag(tag) + with repo_write_lock(self.repo): + for tag in self.repo.tags: + if int(tag.name) > 1: + self.repo.delete_tag(tag) def switch_version(self, target, hard=False): """ @@ -150,9 +167,15 @@ def switch_version(self, target, hard=False): if commit.hexsha == target ]: return - self.clean_branch() - checkout(self.repo, target, hard) + with repo_write_lock(self.repo): + self.clean_branch() + if hard: + logger.debug(f"GIT : RESET HARD {target}") + self.repo.git.reset("--hard", target) + else: + logger.debug(f"GIT : CHECKOUT {target}") + self.repo.git.checkout(target) def commit_changes(self, message): """ @@ -160,19 +183,23 @@ def commit_changes(self, message): :param message: string message to insert in commit or tag """ logger.debug("GIT : COMMIT CHANGES ON SAVE") - if not self.repo.tags: - self.repo.git.add("*") - self.repo.git.commit("-m", message) - self.create_version(message) - elif self.repo.git.diff("--name-only"): - self.repo.git.add("*") - self.repo.git.commit("-m", message) + with repo_write_lock(self.repo): + if not self.repo.tags: + self.repo.git.add("*") + self.repo.git.commit("-m", message) + self.repo.create_tag( + datetime.now().strftime("%Y-%m-%d-%H-%M-%S"), message=message + ) + elif self.repo.git.diff("--name-only"): + self.repo.git.add("*") + self.repo.git.commit("-m", message) def create_publication_commit(self, message): logger.debug("GIT : CREATE PULICATION COMMIT AS NEW VERSION") - self.repo.git.add("*") - self.repo.git.commit("-m", message) - self.create_version(message) + with repo_write_lock(self.repo): + self.repo.git.add("*") + self.repo.git.commit("-m", message) + self.repo.create_tag(datetime.now().strftime("%Y-%m-%d-%H-%M-%S"), message=message) def get_version(self, name): """