@@ -29,6 +29,9 @@ public class RecalculateModMultipliersCommand
2929 [ Option ( CommandOptionType . SingleValue , Template = "--batch-size" ) ]
3030 public int BatchSize { get ; set ; } = 5000 ;
3131
32+ [ Option ( CommandOptionType . SingleValue , Template = "--flush-size" ) ]
33+ public int FlushSize { get ; set ; } = 1000 ;
34+
3235 [ Option ( CommandOptionType . SingleOrNoValue , Template = "--dry-run" ) ]
3336 public bool DryRun { get ; set ; }
3437
@@ -41,12 +44,12 @@ public class RecalculateModMultipliersCommand
4144 [ Option ( CommandOptionType . SingleOrNoValue , Template = "--run-indexing" ) ]
4245 public bool RunIndexing { get ; set ; }
4346
44- private readonly StringBuilder sqlBuffer = new StringBuilder ( ) ;
45-
4647 private ElasticQueuePusher ? elasticQueuePusher ;
4748
4849 private readonly List < ElasticQueuePusher . ElasticScoreItem > elasticItems = new List < ElasticQueuePusher . ElasticScoreItem > ( ) ;
4950
51+ private readonly List < ( ulong id , long newTotalScore ) > pendingUpdates = new List < ( ulong id , long newTotalScore ) > ( ) ;
52+
5053 [ UsedImplicitly ]
5154 public async Task < int > OnExecuteAsync ( CancellationToken cancellationToken )
5255 {
@@ -94,6 +97,8 @@ public async Task<int> OnExecuteAsync(CancellationToken cancellationToken)
9497
9598 foreach ( var score in scoresWithMods )
9699 {
100+ cancellationToken . ThrowIfCancellationRequested ( ) ;
101+
97102 string source = score . is_legacy_score ? "stable" : "lazer " ;
98103
99104 var beatmap = await BeatmapStore . GetBeatmapAsync ( score . beatmap_id , conn ) ;
@@ -156,10 +161,12 @@ public async Task<int> OnExecuteAsync(CancellationToken cancellationToken)
156161 if ( Verbose )
157162 Console . WriteLine ( $ "[{ score . id , 11 } { source } ] Updating score: { oldTotalScore , 8 } (old) -> { newTotalScore , 8 } (new)") ;
158163
159- sqlBuffer . Append ( $@ "UPDATE `scores` SET `total_score` = { newTotalScore } WHERE `id` = { score . id } ;" ) ;
164+ pendingUpdates . Add ( ( score . id , newTotalScore ) ) ;
160165 if ( RunIndexing )
161166 elasticItems . Add ( new ElasticQueuePusher . ElasticScoreItem { ScoreId = ( long ? ) score . id } ) ;
162167 updated ++ ;
168+
169+ flush ( conn ) ;
163170 }
164171
165172 lastId += ( ulong ) BatchSize ;
@@ -174,20 +181,31 @@ public async Task<int> OnExecuteAsync(CancellationToken cancellationToken)
174181 return 0 ;
175182 }
176183
184+ private readonly StringBuilder statementBuilder = new StringBuilder ( ) ;
185+
177186 private void flush ( MySqlConnection conn , bool force = false )
178187 {
179- int bufferLength = sqlBuffer . Length ;
180-
181- if ( bufferLength == 0 )
182- return ;
183-
184- if ( bufferLength > 1024 || force )
188+ if ( pendingUpdates . Count >= FlushSize || force )
185189 {
186190 if ( ! DryRun )
187191 {
188- Console . WriteLine ( ) ;
189- Console . WriteLine ( $ "Flushing sql batch ({ bufferLength : N0} bytes)") ;
190- conn . Execute ( sqlBuffer . ToString ( ) ) ;
192+ if ( pendingUpdates . Count > 0 )
193+ {
194+ Console . WriteLine ( ) ;
195+ Console . WriteLine ( $ "Flushing sql updates ({ pendingUpdates . Count : N0} rows)") ;
196+
197+ statementBuilder . Clear ( ) ;
198+ statementBuilder . AppendLine ( "UPDATE `scores` SET `total_score` = CASE `id`" ) ;
199+
200+ foreach ( var row in pendingUpdates )
201+ statementBuilder . AppendLine ( $ "WHEN { row . id } THEN { row . newTotalScore } ") ;
202+
203+ statementBuilder . AppendLine ( "END WHERE `id` IN (" ) ;
204+ statementBuilder . AppendLine ( string . Join ( ',' , pendingUpdates . Select ( u => u . id ) ) ) ;
205+ statementBuilder . AppendLine ( ")" ) ;
206+
207+ conn . Execute ( statementBuilder . ToString ( ) ) ;
208+ }
191209
192210 if ( RunIndexing && elasticItems . Count > 0 )
193211 {
@@ -197,7 +215,8 @@ private void flush(MySqlConnection conn, bool force = false)
197215 }
198216
199217 elasticItems . Clear ( ) ;
200- sqlBuffer . Clear ( ) ;
218+ statementBuilder . Clear ( ) ;
219+ pendingUpdates . Clear ( ) ;
201220 }
202221 }
203222 }
0 commit comments