
org.conqat.engine.index.shared.GitWriteLockManager Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of teamscale-commons Show documentation
Show all versions of teamscale-commons Show documentation
Provides common DTOs for Teamscale
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