diff --git a/tools/test-proxy/Azure.Sdk.Tools.TestProxy/Store/GitStore.cs b/tools/test-proxy/Azure.Sdk.Tools.TestProxy/Store/GitStore.cs
index e81b36d3272..030572103d1 100644
--- a/tools/test-proxy/Azure.Sdk.Tools.TestProxy/Store/GitStore.cs
+++ b/tools/test-proxy/Azure.Sdk.Tools.TestProxy/Store/GitStore.cs
@@ -43,6 +43,21 @@ public class GitStore : IAssetsStore
public GitStoreBreadcrumb BreadCrumb = new GitStoreBreadcrumb();
+ ///
+ /// We need to lock repo inititialization behind a queue.
+ /// This is due to the fact that Restore() can be called from multiple parallel
+ /// requests, as multiple "startplayback" can be firing at the same time.
+ ///
+ /// While the Restore() action itself is idempotent, the Initialization of the assets repo
+ /// is NOT. We will use this queue to force ONE single initialization at a time.
+ ///
+ /// We don't want to gate ALL initializations behind the same gate though. We can restore
+ /// multiple DIFFERENT assets.jsons at the same time. It's specifically when two restores for the SAME
+ /// assets.json are fired that we run into problems.
+ ///
+ /// Everything else will still run in parallel.
+ ///
+ private ConcurrentDictionary InitTasks = new ConcurrentDictionary();
public ConcurrentDictionary Assets = new ConcurrentDictionary();
@@ -453,34 +468,39 @@ public bool IsAssetsRepoInitialized(GitAssetsConfiguration config)
///
public bool InitializeAssetsRepo(GitAssetsConfiguration config, bool forceInit = false)
{
- var assetRepo = config.AssetsRepoLocation;
- var initialized = IsAssetsRepoInitialized(config);
var workCompleted = false;
+ var initQueue = InitTasks.GetOrAdd(config.AssetsRepoLocation, new TaskQueue());
- if (forceInit)
+ initQueue.Enqueue(() =>
{
- DirectoryHelper.DeleteGitDirectory(assetRepo.ToString());
- Directory.CreateDirectory(assetRepo.ToString());
- initialized = false;
- }
+ var assetRepo = config.AssetsRepoLocation;
+ var initialized = IsAssetsRepoInitialized(config);
- if (!initialized)
- {
- try
+ if (forceInit)
{
- var cloneUrl = GetCloneUrl(config.AssetsRepo, config.RepoRoot);
- // The -c core.longpaths=true is basically for Windows and is a noop for other platforms
- GitHandler.Run($"clone -c core.longpaths=true --no-checkout --filter=tree:0 {cloneUrl} .", config);
- GitHandler.Run($"sparse-checkout init", config);
+ DirectoryHelper.DeleteGitDirectory(assetRepo.ToString());
+ Directory.CreateDirectory(assetRepo.ToString());
+ initialized = false;
}
- catch(GitProcessException e)
+
+ if (!initialized)
{
- throw GenerateInvokeException(e.Result);
- }
+ try
+ {
+ var cloneUrl = GetCloneUrl(config.AssetsRepo, config.RepoRoot);
+ // The -c core.longpaths=true is basically for Windows and is a noop for other platforms
+ GitHandler.Run($"clone -c core.longpaths=true --no-checkout --filter=tree:0 {cloneUrl} .", config);
+ GitHandler.Run($"sparse-checkout init", config);
+ }
+ catch (GitProcessException e)
+ {
+ throw GenerateInvokeException(e.Result);
+ }
- CheckoutRepoAtConfig(config, cleanEnabled: false);
- workCompleted = true;
- }
+ CheckoutRepoAtConfig(config, cleanEnabled: false);
+ workCompleted = true;
+ }
+ });
return workCompleted;
}