From 90605034ac1e92b17f37fc007d51383c3b7f453e Mon Sep 17 00:00:00 2001 From: Robin Munn Date: Thu, 9 May 2024 18:05:24 +0700 Subject: [PATCH] Try new approach to invalidating NFS cache This time we wait until hgweb sees the same view of the repo that we think it should see, e.g. empty or non-empty. --- backend/LexBoxApi/Services/HgService.cs | 40 ++++++++++++++++--- .../LexCore/ServiceInterfaces/IHgService.cs | 1 + 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/backend/LexBoxApi/Services/HgService.cs b/backend/LexBoxApi/Services/HgService.cs index 3d6d517c5..416622e3d 100644 --- a/backend/LexBoxApi/Services/HgService.cs +++ b/backend/LexBoxApi/Services/HgService.cs @@ -23,6 +23,8 @@ public partial class HgService : IHgService private const string DELETED_REPO_FOLDER = "_____deleted_____"; private const string TEMP_REPO_FOLDER = "_____temp_____"; + private const string AllZeroHash = "0000000000000000000000000000000000000000"; + private readonly IOptions _options; private readonly Lazy _hgClient; private readonly ILogger _logger; @@ -67,7 +69,7 @@ await Task.Run(() => { InitRepoAt(new DirectoryInfo(PrefixRepoFilePath(code))); }); - await InvalidateDirCache(code); + await WaitForRepoEmptyState(code, RepoEmptyState.Empty); } private void InitRepoAt(DirectoryInfo repoDirectory) @@ -83,7 +85,6 @@ private void InitRepoAt(DirectoryInfo repoDirectory) public async Task DeleteRepo(string code) { await Task.Run(() => Directory.Delete(PrefixRepoFilePath(code), true)); - await InvalidateDirCache(code); } public BackupExecutor? BackupRepo(string code) @@ -106,7 +107,8 @@ public async Task ResetRepo(string code) await SoftDeleteRepo(code, $"{FileUtils.ToTimestamp(DateTimeOffset.UtcNow)}__reset"); //we must init the repo as uploading a zip is optional tmpRepo.MoveTo(PrefixRepoFilePath(code)); - await InvalidateDirCache(code); + // await InvalidateDirCache(code); // TODO 789: Sometimes NFS hasn't finished the MoveTo above! So we need to find a way to wait until InvalidateDirCache sees an *empty* repo... + await WaitForRepoEmptyState(code, RepoEmptyState.Empty); } public async Task FinishReset(string code, Stream zipFile) @@ -140,7 +142,8 @@ await Task.Run(() => // Now we're ready to move the new repo into place, replacing the old one await DeleteRepo(code); tempRepo.MoveTo(PrefixRepoFilePath(code)); - await InvalidateDirCache(code); + // await InvalidateDirCache(code); + await WaitForRepoEmptyState(code, RepoEmptyState.NonEmpty); // TODO: Either catch the case where someone uploaded a .zip of an empty .hg repo, or set a timeout in WaitForRepoEmptyState } /// @@ -185,7 +188,6 @@ await Task.Run(() => PrefixRepoFilePath(code), Path.Combine(deletedRepoPath, deletedRepoName)); }); - await InvalidateDirCache(code); } private const UnixFileMode Permissions = UnixFileMode.GroupRead | UnixFileMode.GroupWrite | @@ -272,6 +274,28 @@ public Task InvalidateDirCache(string code) return ExecuteHgCommandServerCommand(code, "invalidatedircache", default); } + public async Task GetTipHash(string code) + { + var content = await ExecuteHgCommandServerCommand(code, "tip", default); + return await content.ReadAsStringAsync(); + } + + public async Task WaitForRepoEmptyState(string code, RepoEmptyState expectedState) + { + // TODO: Set timeout so uploading a zip of an empty .hg repo doesn't cause infinite loop here + var done = false; + while (!done) + { + var hash = await GetTipHash(code); + var isEmpty = hash == AllZeroHash; + done = expectedState switch + { + RepoEmptyState.Empty => isEmpty, + RepoEmptyState.NonEmpty => !isEmpty + }; + } + } + public async Task GetLexEntryCount(string code, ProjectType projectType) { var command = projectType switch @@ -388,3 +412,9 @@ public class BrowseResponse { public BrowseFilesResponse[]? Files { get; set; } } + +public enum RepoEmptyState +{ + Empty, + NonEmpty +} diff --git a/backend/LexCore/ServiceInterfaces/IHgService.cs b/backend/LexCore/ServiceInterfaces/IHgService.cs index 5da6db548..dcded69a6 100644 --- a/backend/LexCore/ServiceInterfaces/IHgService.cs +++ b/backend/LexCore/ServiceInterfaces/IHgService.cs @@ -15,6 +15,7 @@ public interface IHgService Task ResetRepo(string code); Task FinishReset(string code, Stream zipFile); Task VerifyRepo(string code, CancellationToken token); + Task GetTipHash(string code); Task GetLexEntryCount(string code, ProjectType projectType); Task GetRepositoryIdentifier(Project project); Task ExecuteHgRecover(string code, CancellationToken token);