Skip to content

Commit

Permalink
Try new approach to invalidating NFS cache
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
rmunn committed May 9, 2024
1 parent 3f827e7 commit 9060503
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 5 deletions.
40 changes: 35 additions & 5 deletions backend/LexBoxApi/Services/HgService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<HgConfig> _options;
private readonly Lazy<HttpClient> _hgClient;
private readonly ILogger<HgService> _logger;
Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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)
Expand Down Expand Up @@ -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
}

/// <summary>
Expand Down Expand Up @@ -185,7 +188,6 @@ await Task.Run(() =>
PrefixRepoFilePath(code),
Path.Combine(deletedRepoPath, deletedRepoName));
});
await InvalidateDirCache(code);
}

private const UnixFileMode Permissions = UnixFileMode.GroupRead | UnixFileMode.GroupWrite |
Expand Down Expand Up @@ -272,6 +274,28 @@ public Task<HttpContent> InvalidateDirCache(string code)
return ExecuteHgCommandServerCommand(code, "invalidatedircache", default);
}

public async Task<string> 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

Check warning on line 291 in backend/LexBoxApi/Services/HgService.cs

View workflow job for this annotation

GitHub Actions / Build API / publish-api

The switch expression does not handle some values of its input type (it is not exhaustive) involving an unnamed enum value. For example, the pattern '(LexBoxApi.Services.RepoEmptyState)2' is not covered.

Check warning on line 291 in backend/LexBoxApi/Services/HgService.cs

View workflow job for this annotation

GitHub Actions / Build API / publish-api

The switch expression does not handle some values of its input type (it is not exhaustive) involving an unnamed enum value. For example, the pattern '(LexBoxApi.Services.RepoEmptyState)2' is not covered.
{
RepoEmptyState.Empty => isEmpty,
RepoEmptyState.NonEmpty => !isEmpty
};
}
}

public async Task<int?> GetLexEntryCount(string code, ProjectType projectType)
{
var command = projectType switch
Expand Down Expand Up @@ -388,3 +412,9 @@ public class BrowseResponse
{
public BrowseFilesResponse[]? Files { get; set; }
}

public enum RepoEmptyState
{
Empty,
NonEmpty
}
1 change: 1 addition & 0 deletions backend/LexCore/ServiceInterfaces/IHgService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public interface IHgService
Task ResetRepo(string code);
Task FinishReset(string code, Stream zipFile);
Task<HttpContent> VerifyRepo(string code, CancellationToken token);
Task<string> GetTipHash(string code);
Task<int?> GetLexEntryCount(string code, ProjectType projectType);
Task<string?> GetRepositoryIdentifier(Project project);
Task<HttpContent> ExecuteHgRecover(string code, CancellationToken token);
Expand Down

0 comments on commit 9060503

Please sign in to comment.