Skip to content

Commit

Permalink
Store repo size (in kB) in database (#1221)
Browse files Browse the repository at this point in the history
* Add hg command to get repo size in kB
* Add DB migration for RepoSizeInKb column
* Update project repo size after any Send/Receive
* GQL mutation to update project repo size

This one is open to anyone who can view the project, so that frontend
code can automatically look up the project size if it's missing, with no
manual button clicks necessary.

* Show repo size (in kB) on project page
* Report size in megabytes, might be fractional

Only use one digit after the decimal point; nobody cares if a project's
size is 766.734 MB. 766.7 MB is more than precise enough.

---------

Co-authored-by: Tim Haasdyk <[email protected]>
  • Loading branch information
rmunn and myieye authored Nov 15, 2024
1 parent a5309ad commit 10a107f
Show file tree
Hide file tree
Showing 15 changed files with 1,554 additions and 5 deletions.
2 changes: 1 addition & 1 deletion .config/dotnet-tools.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"isRoot": true,
"tools": {
"dotnet-ef": {
"version": "8.0.8",
"version": "8.0.10",
"commands": [
"dotnet-ef"
]
Expand Down
18 changes: 18 additions & 0 deletions backend/LexBoxApi/GraphQL/ProjectMutations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,24 @@ public async Task<IQueryable<Project>> SetRetentionPolicy(
return dbContext.Projects.Where(p => p.Id == input.ProjectId);
}

[Error<NotFoundException>]
[Error<DbError>]
[Error<UnauthorizedAccessException>]
[UseMutationConvention]
[UseFirstOrDefault]
[UseProjection]
public async Task<IQueryable<Project>> UpdateProjectRepoSizeInKb(string code,
IPermissionService permissionService,
ProjectService projectService,
LexBoxDbContext dbContext)
{
var projectId = await projectService.LookupProjectId(code);
await permissionService.AssertCanViewProject(projectId);
if (projectId == default) throw NotFoundException.ForType<Project>();
await projectService.UpdateRepoSizeInKb(code);
return dbContext.Projects.Where(p => p.Id == projectId);
}

[Error<NotFoundException>]
[Error<DbError>]
[Error<UnauthorizedAccessException>]
Expand Down
7 changes: 7 additions & 0 deletions backend/LexBoxApi/Services/HgService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,13 @@ public async Task<string> GetTipHash(ProjectCode code, CancellationToken token =
return await content.ReadAsStringAsync();
}

public async Task<int?> GetRepoSizeInKb(ProjectCode code, CancellationToken token = default)
{
var content = await ExecuteHgCommandServerCommand(code, "reposizeinkb", token);
var sizeStr = await content.ReadAsStringAsync();
return int.TryParse(sizeStr, out var size) ? size : null;
}

private async Task WaitForRepoEmptyState(ProjectCode code, RepoEmptyState expectedState, int timeoutMs = 30_000, CancellationToken token = default)
{
// Set timeout so unforeseen errors can't cause an infinite loop
Expand Down
12 changes: 12 additions & 0 deletions backend/LexBoxApi/Services/ProjectService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ public async Task ResetProject(ResetProjectByAdminInput input)
var rowsAffected = await dbContext.Projects.Where(p => p.Code == input.Code && p.ResetStatus == ResetStatus.None)
.ExecuteUpdateAsync(u => u
.SetProperty(p => p.ResetStatus, ResetStatus.InProgress)
.SetProperty(p => p.RepoSizeInKb, 0)
.SetProperty(p => p.LastCommit, null as DateTimeOffset?));
if (rowsAffected == 0) throw new NotFoundException($"project {input.Code} not ready for reset, either already reset or not found", nameof(Project));
await ResetLexEntryCount(input.Code);
Expand Down Expand Up @@ -239,9 +240,20 @@ public async Task UpdateProjectMetadata(Project project)
}

project.LastCommit = await hgService.GetLastCommitTimeFromHg(project.Code);
project.RepoSizeInKb = await hgService.GetRepoSizeInKb(project.Code);
// Caller is responsible for caling dbContext.SaveChangesAsync()
}

public async Task<int?> UpdateRepoSizeInKb(string projectCode)
{
var project = await dbContext.Projects.FirstOrDefaultAsync(p => p.Code == projectCode);
if (project is null) return null;
var size = await hgService.GetRepoSizeInKb(projectCode);
project.RepoSizeInKb = size;
await dbContext.SaveChangesAsync();
return size;
}

public async Task ResetLexEntryCount(string projectCode)
{
var project = await dbContext.Projects
Expand Down
1 change: 1 addition & 0 deletions backend/LexCore/Entities/Project.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public class Project : EntityBase
public required List<ProjectUsers> Users { get; set; }
public required List<Organization> Organizations { get; set; }
public required DateTimeOffset? LastCommit { get; set; }
public int? RepoSizeInKb { get; set; }
public DateTimeOffset? DeletedDate { get; set; }
public ResetStatus ResetStatus { get; set; } = ResetStatus.None;

Expand Down
1 change: 1 addition & 0 deletions backend/LexCore/ServiceInterfaces/IHgService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public interface IHgService
Task FinishReset(ProjectCode code, Stream zipFile);
Task<HttpContent> VerifyRepo(ProjectCode code, CancellationToken token);
Task<string> GetTipHash(ProjectCode code, CancellationToken token = default);
Task<int?> GetRepoSizeInKb(ProjectCode code, CancellationToken token = default);
Task<int?> GetLexEntryCount(ProjectCode code, ProjectType projectType);
Task<string?> GetRepositoryIdentifier(Project project);
Task<HttpContent> ExecuteHgRecover(ProjectCode code, CancellationToken token);
Expand Down
Loading

0 comments on commit 10a107f

Please sign in to comment.