Skip to content

Commit f5e67a9

Browse files
authored
Merge pull request #357 from peppy/start-deleting-proactively
2 parents fd4a03a + 2c24ced commit f5e67a9

File tree

1 file changed

+47
-25
lines changed

1 file changed

+47
-25
lines changed

osu.Server.Queues.ScoreStatisticsProcessor/Commands/Maintenance/DeleteNonPreservedScoresCommand.cs

Lines changed: 47 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System;
55
using System.Globalization;
6+
using System.Linq;
67
using System.Net;
78
using System.Threading;
89
using System.Threading.Tasks;
@@ -15,7 +16,7 @@
1516

1617
namespace osu.Server.Queues.ScoreStatisticsProcessor.Commands.Maintenance
1718
{
18-
[Command("cleanup", Description = "Delete non-preserved scores which are stale enough.")]
19+
[Command("delete-non-preserved", Description = "Delete non-preserved scores which are stale enough.")]
1920
public class DeleteNonPreservedScoresCommand
2021
{
2122
/// <summary>
@@ -30,44 +31,65 @@ public async Task<int> OnExecuteAsync(CancellationToken cancellationToken)
3031
using (var deleteCommand = deleteConnection.CreateCommand())
3132
using (var s3 = S3.GetClient())
3233
{
34+
// TODO: for safety do we want to delete pins here? might be a race condition where user pins right as this process is running.
3335
deleteCommand.CommandText = "DELETE FROM scores WHERE id = @id;";
3436

3537
MySqlParameter scoreId = deleteCommand.Parameters.Add("id", MySqlDbType.UInt64);
3638

3739
await deleteCommand.PrepareAsync(cancellationToken);
3840

39-
var scores = await readConnection.QueryAsync<SoloScore>(new CommandDefinition(
40-
$"SELECT * FROM `scores` WHERE `preserve` = 0 AND `unix_updated_at` < UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL {preserve_hours} HOUR))", flags: CommandFlags.None, cancellationToken: cancellationToken));
41-
42-
foreach (var score in scores)
41+
while (!cancellationToken.IsCancellationRequested)
4342
{
44-
if (cancellationToken.IsCancellationRequested)
45-
break;
43+
var scores = await readConnection.QueryAsync<SoloScore>(new CommandDefinition(
44+
$"SELECT * FROM `scores` WHERE `preserve` = 0 AND `unix_updated_at` < UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL {preserve_hours} HOUR)) LIMIT 1000",
45+
cancellationToken: cancellationToken));
4646

47-
Console.WriteLine($"Deleting score {score.id}...");
48-
scoreId.Value = score.id;
49-
await deleteCommand.ExecuteNonQueryAsync(cancellationToken);
47+
if (!scores.Any())
48+
break;
5049

51-
// TODO: check pins
50+
Console.WriteLine($"Processing next batch of {scores.Count()} scores");
5251

53-
if (score.has_replay)
52+
foreach (var score in scores)
5453
{
55-
Console.WriteLine("* Removing replay from S3...");
56-
var deleteResult = await s3.DeleteObjectAsync(S3.REPLAYS_BUCKET, score.id.ToString(CultureInfo.InvariantCulture), cancellationToken);
54+
if (cancellationToken.IsCancellationRequested)
55+
break;
56+
57+
Console.WriteLine($"Deleting score {score.id}...");
5758

58-
switch (deleteResult.HttpStatusCode)
59+
if (score.has_replay)
5960
{
60-
case HttpStatusCode.NoContent:
61-
// below wording is intentionally very roundabout, because s3 does not actually really seem to produce the types of error you'd expect.
62-
// for instance, even if you request removal of a nonexistent object, it'll just throw a 204 No Content back
63-
// with no real way to determine whether it actually even did anything.
64-
Console.WriteLine("* Deletion request completed without error.");
65-
break;
66-
67-
default:
68-
await Console.Error.WriteLineAsync($"* Received unexpected status code when attempting to delete replay: {deleteResult.HttpStatusCode}.");
69-
break;
61+
if (score.is_legacy_score)
62+
{
63+
// TODO: we likely do want logic here to handle the cleanup of web-10 (at least the replay part?).
64+
// for now, make sure we don't attempt to clean up stable scores with replays here.
65+
throw new InvalidOperationException($"Legacy score id:{score.id} legacy_id:{score.legacy_score_id} has replay flag set");
66+
}
67+
68+
Console.WriteLine("* Removing replay from S3...");
69+
var deleteResult = await s3.DeleteObjectAsync(S3.REPLAYS_BUCKET, score.id.ToString(CultureInfo.InvariantCulture), cancellationToken);
70+
71+
switch (deleteResult.HttpStatusCode)
72+
{
73+
case HttpStatusCode.NoContent:
74+
// below wording is intentionally very roundabout, because s3 does not actually really seem to produce the types of error you'd expect.
75+
// for instance, even if you request removal of a nonexistent object, it'll just throw a 204 No Content back
76+
// with no real way to determine whether it actually even did anything.
77+
Console.WriteLine("* Deletion request completed without error.");
78+
break;
79+
80+
default:
81+
await Console.Error.WriteLineAsync($"* Received unexpected status code when attempting to delete replay: {deleteResult.HttpStatusCode}.");
82+
break;
83+
}
7084
}
85+
86+
// TODO: as long as we're doing partition cycling, this is redundant.
87+
// in fact, we could move this inside the replay check. or we could update the initial query to only return `has_replay = 1` for cleanup.
88+
//
89+
// said another way, this whole method exists for the sole purpose of deleting attached replay data.
90+
// deleting the score is only really useful here as a marker that we've cleaned up the replay.
91+
scoreId.Value = score.id;
92+
await deleteCommand.ExecuteNonQueryAsync(cancellationToken);
7193
}
7294
}
7395
}

0 commit comments

Comments
 (0)