Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Common/Constants/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
public static class Duration
{
public static readonly TimeSpan AuditRetentionTime = TimeSpan.FromDays(90);
public static readonly TimeSpan ShockerControlLogRetentionTime = TimeSpan.FromDays(365);

public static readonly TimeSpan PasswordResetRequestLifetime = TimeSpan.FromHours(1);

Expand Down
2 changes: 2 additions & 0 deletions Common/Constants/HardLimits.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,6 @@ public static class HardLimits
public const int ShockerControlLogCustomNameMaxLength = 64;

public const int CreateShareRequestMaxShockers = 128;

public const int MaxShockerControlLogsPerUser = 2048;
}
70 changes: 70 additions & 0 deletions Cron/Jobs/ClearOldShockerControlLogs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
using Microsoft.EntityFrameworkCore;
using OpenShock.Common;
using OpenShock.Common.Constants;
using OpenShock.Common.OpenShockDb;
using OpenShock.Cron.Attributes;

namespace OpenShock.Cron.Jobs;

/// <summary>
/// Deletes shocker control logs older than the retention period and enforces a maximum log count
/// </summary>
[CronJob("0 0 * * *")] // Every day at midnight (https://crontab.guru/)
public sealed class ClearOldShockerControlLogs
{
private readonly OpenShockContext _db;
private readonly ILogger<ClearOldShockerControlLogs> _logger;

/// <summary>
/// DI constructor
/// </summary>
/// <param name="db"></param>
/// <param name="logger"></param>
public ClearOldShockerControlLogs(OpenShockContext db, ILogger<ClearOldShockerControlLogs> logger)
{
_db = db;
_logger = logger;
}

public async Task Execute()
{
// Calculate the retention threshold based on configured retention time.
var retentionThreshold = DateTime.UtcNow - Duration.ShockerControlLogRetentionTime;

// Delete logs older than the retention threshold.
var deletedByAge = await _db.ShockerControlLogs
.Where(log => log.CreatedOn < retentionThreshold)
.ExecuteDeleteAsync();

_logger.LogInformation("Deleted {deletedCount} shocker control logs older than {retentionThreshold:O}.", deletedByAge, retentionThreshold);

var userLogsCounts = await _db.ShockerControlLogs
.GroupBy(log => log.Shocker.DeviceNavigation.Owner)
.Select(group => new
{
UserId = group.Key,
CountToDelete = Math.Max(0, group.Count() - HardLimits.MaxShockerControlLogsPerUser),
DeleteBeforeDate = group
.OrderByDescending(log => log.CreatedOn)
.Skip(HardLimits.MaxShockerControlLogsPerUser)
.Select(log => log.CreatedOn)
.FirstOrDefault()
})
.Where(result => result.CountToDelete > 0)
.ToArrayAsync();

if (userLogsCounts.Length != 0)
{
_logger.LogInformation("A total of {totalLogsToDelete} logs will be deleted to enforce per-user limits.", userLogsCounts.Sum(x => x.CountToDelete));

foreach (var userLogCount in userLogsCounts)
{
await _db.ShockerControlLogs
.Where(log => log.Shocker.DeviceNavigation.Owner == userLogCount.UserId && log.CreatedOn < userLogCount.DeleteBeforeDate)
.ExecuteDeleteAsync();
}
}

_logger.LogInformation("Done!");
}
}
Loading