All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.conqat.engine.index.shared.GitWriteLockManager Maven / Gradle / Ivy

There is a newer version: 2025.3.0-rc2
Show newest version
package org.conqat.engine.index.shared;

import java.io.File;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import org.conqat.lib.commons.concurrent.ThreadUtils;

/**
 * Class for coordinating concurrent writes to local git clones.
 *
 * We only lock write operations, as we expect that reading can happen fully
 * concurrent (see link below).
 *
 * @see "https://stackoverflow.com/questions/13039150/are-concurrent-operations-possible-with-git-repositories"
 */
public class GitWriteLockManager {

	/**
	 * Name of the "special" file used for marking pending operations in git.
	 *
	 * @see "https://www.pluralsight.com/guides/understanding-and-using-git%27s-index.lock-file"
	 */
	private static final String INDEX_LOCK_FILE_NAME = "index.lock";

	private static final long MAX_LOCK_FILE_WAIT_TIME_SECONDS = 15 * 60;

	private static final Map EXISTING_LOCKS = Collections.synchronizedMap(new HashMap<>());

	/**
	 * Attempts to lock the given directory for writing. The returned {@link Lock}
	 * is already locked. The caller should call {@link Lock#unlock()} when
	 * finished.
	 *
	 * This works on local locks (i.e. managed within the process). In addition, we
	 * check for the "index.lock" file to make the best effort to also handle
	 * locking across processes, i.e. when multiple Teamscale instances share one
	 * git repository (network share or similar). This lock file is typically
	 * created before any git operation modifies the index (e.g. a fetch or gc), and
	 * removed afterwards, to prevent concurrent modification and hence corruption
	 * of the index files.
	 *
	 * @see "https://www.pluralsight.com/guides/understanding-and-using-git%27s-index.lock-file"
	 */
	public static Lock lockGitDirectoryForWriting(File gitMetadataDirectory) {
		Lock lock = EXISTING_LOCKS.computeIfAbsent(gitMetadataDirectory, x -> new ReentrantLock());
		lock.lock();

		// check if another process has created a lock file
		File lockFile = new File(gitMetadataDirectory, INDEX_LOCK_FILE_NAME);
		long maxWaitTime = System.currentTimeMillis() + MAX_LOCK_FILE_WAIT_TIME_SECONDS * 1000L;
		while (lockFile.exists() && System.currentTimeMillis() < maxWaitTime) {
			ThreadUtils.sleep(10);
		}

		return lock;
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy