@@ -103,19 +103,78 @@ internal async ValueTask<Result<ArchiveCommandResult>> Handle(HandlerContext han
103103
104104 // Attach cancellation logic
105105 tasks . Values . RaiseCancellationOnFault ( errorCancellationTokenSource ) ;
106+
107+ try
106108 {
109+ await Task . WhenAll ( tasks . Values ) ;
110+ }
111+ catch ( OperationCanceledException ) when ( cancellationToken . IsCancellationRequested && ! errorCancellationTokenSource . IsCancellationRequested )
112+ {
113+ // User-triggered cancellation
114+ logger . LogInformation ( "Archive operation cancelled by user" ) ;
115+ return Result . Fail ( "Archive operation was cancelled by user" ) ;
116+ }
117+ catch ( Exception ex )
118+ {
119+ // Either a task failed with an exception or error-triggered cancellation occurred
120+ logger . LogError ( ex , "Unhandled exception: Either a task failed with an exception or error-triggered cancellation occurred" ) ;
121+
122+ var faultedTasks = tasks . Where ( kvp => kvp . Value . IsFaulted ) . Select ( kvp => ( Name : kvp . Key , Exception : kvp . Value . Exception ! . GetBaseException ( ) ) ) . ToArray ( ) ;
123+
124+ // Trigger error-driven cancellation to signal other tasks to stop gracefully
125+ errorCancellationTokenSource . Cancel ( ) ;
126+
127+ // Wait for all tasks to complete gracefully
128+ await Task . WhenAll ( tasks . Values . Select ( async t =>
107129 {
130+ try
108131 {
132+ await t ;
109133 }
134+ catch
135+ {
136+ /* Ignore exceptions during graceful shutdown */
137+ }
138+ } ) ) ;
139+
140+ // Observe all task exceptions to prevent UnobservedTaskException
141+ foreach ( var task in tasks . Values . Where ( t => t . IsFaulted ) )
142+ {
143+ _ = task . Exception ;
144+ }
145+
146+ // Log cancelled tasks (debug level)
147+ var cancelledTaskNames = tasks . Where ( kvp => kvp . Value . IsCanceled ) . Select ( kvp => kvp . Key ) . ToArray ( ) ;
148+ if ( cancelledTaskNames . Any ( ) )
149+ {
150+ logger . LogDebug ( "Tasks cancelled during graceful shutdown: {TaskNames}" , string . Join ( ", " , cancelledTaskNames ) ) ;
151+ }
152+
153+ // Log and handle failed tasks (error level)
154+ if ( faultedTasks is { Length : 1 } && faultedTasks . Single ( ) is var faultedTask )
155+ {
156+ // Single faulted task - return the exception
157+ var msg = faultedTask . Exception ? . GetBaseException ( ) . Message ?? "UNKNOWN" ;
158+ logger . LogError ( faultedTask . Exception , "Task '{TaskName}' failed with exception '{Exception}'" , faultedTask . Name , msg ) ;
159+ return Result . Fail ( $ "Archive operation failed: { faultedTask . Name } failed with { msg } ") . WithError ( new ExceptionalError ( faultedTask . Exception ) ) ;
160+ }
161+ else
162+ {
163+ // Multiple faulted tasks - return aggregate exception
164+ var exceptions = faultedTasks . Select ( ft => ft . Exception ) . ToArray ( ) ;
165+ var aggregateException = new AggregateException ( "Multiple tasks failed during archive operation" , exceptions ) ;
166+ var faultedTaskNames = string . Join ( ", " , faultedTasks . Select ( ft => ft . Name ) ) ;
167+ logger . LogError ( aggregateException , "Tasks failed: {FaultedTaskNames}" , faultedTaskNames ) ;
168+ return Result . Fail ( $ "Archive operation failed: { faultedTaskNames } tasks failed") . WithError ( new ExceptionalError ( aggregateException ) ) ;
169+ }
110170 }
111171
112172 try
113173 {
114- await Task . WhenAll ( tasks . Values ) ;
115-
116174 // 6. Remove PointerFileEntries that do not exist on disk
117- logger . LogDebug ( "Cleaning up pointer file entries that no longer exist on disk" ) ;
175+ logger . LogInformation ( "Cleaning up pointer file entries that no longer exist on disk... " ) ;
118176 pointerFileEntriesDeleted = handlerContext . StateRepository . DeletePointerFileEntries ( pfe => ! handlerContext . FileSystem . FileExists ( pfe . RelativeName ) ) ;
177+ logger . LogInformation ( "Cleaning up pointer file entries that no longer exist on disk... {count} deleted" , pointerFileEntriesDeleted ) ;
119178
120179 // 7. Upload the new state file to blob storage
121180 string ? newStateName = null ;
@@ -150,9 +209,9 @@ internal async ValueTask<Result<ArchiveCommandResult>> Handle(HandlerContext han
150209 TotalLocalFiles = totalLocalFiles ,
151210 ExistingPointerFiles = existingPointerFiles ,
152211
153- ChunksBeforeOperation = statisticsBefore . ChunkCount ,
154- BinariesBeforeOperation = statisticsBefore . BinaryCount ,
155- ArchivedSizeBeforeOperation = statisticsBefore . ArchivedSize ,
212+ ChunksBeforeOperation = statisticsBefore . ChunkCount ,
213+ BinariesBeforeOperation = statisticsBefore . BinaryCount ,
214+ ArchivedSizeBeforeOperation = statisticsBefore . ArchivedSize ,
156215
157216 UniqueBinariesUploaded = uniqueBinariesUploaded ,
158217 UniqueChunksUploaded = uniqueChunksUploaded ,
@@ -161,13 +220,13 @@ internal async ValueTask<Result<ArchiveCommandResult>> Handle(HandlerContext han
161220 PointerFilesCreated = pointerFilesCreated ,
162221 PointerFileEntriesDeleted = pointerFileEntriesDeleted ,
163222
164- ChunksAfterOperation = statisticsAfter . ChunkCount ,
165- BinariesAfterOperation = statisticsAfter . BinaryCount ,
166- ArchivedSizeAfterOperation = statisticsAfter . ArchivedSize ,
223+ ChunksAfterOperation = statisticsAfter . ChunkCount ,
224+ BinariesAfterOperation = statisticsAfter . BinaryCount ,
225+ ArchivedSizeAfterOperation = statisticsAfter . ArchivedSize ,
167226
168- NewStateName = newStateName ,
169- Warnings = warnings . ToArray ( ) ,
170- FilesSkipped = filesSkipped
227+ NewStateName = newStateName ,
228+ Warnings = warnings . ToArray ( ) ,
229+ FilesSkipped = filesSkipped
171230 } ) ;
172231 }
173232 catch ( OperationCanceledException ) when ( cancellationToken . IsCancellationRequested && ! errorCancellationTokenSource . IsCancellationRequested )
@@ -176,52 +235,10 @@ internal async ValueTask<Result<ArchiveCommandResult>> Handle(HandlerContext han
176235 logger . LogInformation ( "Archive operation cancelled by user" ) ;
177236 return Result . Fail ( "Archive operation was cancelled by user" ) ;
178237 }
179- catch ( Exception ex )
238+ catch ( Exception e )
180239 {
181- // Either a task failed with an exception or error-triggered cancellation occurred
182-
183- var faultedTasks = tasks . Where ( kvp => kvp . Value . IsFaulted ) . Select ( kvp => ( Name : kvp . Key , Exception : kvp . Value . Exception ! . GetBaseException ( ) ) ) . ToArray ( ) ;
184-
185- // Trigger error-driven cancellation to signal other tasks to stop gracefully
186- errorCancellationTokenSource . Cancel ( ) ;
187-
188- // Wait for all tasks to complete gracefully
189- await Task . WhenAll ( tasks . Values . Select ( async t =>
190- {
191- try { await t ; }
192- catch { /* Ignore exceptions during graceful shutdown */ }
193- } ) ) ;
194-
195- // Observe all task exceptions to prevent UnobservedTaskException
196- foreach ( var task in tasks . Values . Where ( t => t . IsFaulted ) )
197- {
198- _ = task . Exception ;
199- }
200-
201- // Log cancelled tasks (debug level)
202- var cancelledTaskNames = tasks . Where ( kvp => kvp . Value . IsCanceled ) . Select ( kvp => kvp . Key ) . ToArray ( ) ;
203- if ( cancelledTaskNames . Any ( ) )
204- {
205- logger . LogDebug ( "Tasks cancelled during graceful shutdown: {TaskNames}" , string . Join ( ", " , cancelledTaskNames ) ) ;
206- }
207-
208- // Log and handle failed tasks (error level)
209- if ( faultedTasks is { Length : 1 } && faultedTasks . Single ( ) is var faultedTask )
210- {
211- // Single faulted task - return the exception
212- var msg = faultedTask . Exception ? . GetBaseException ( ) . Message ?? "UNKNOWN" ;
213- logger . LogError ( faultedTask . Exception , "Task '{TaskName}' failed with exception '{Exception}'" , faultedTask . Name , msg ) ;
214- return Result . Fail ( $ "Archive operation failed: { faultedTask . Name } failed with { msg } ") . WithError ( new ExceptionalError ( faultedTask . Exception ) ) ;
215- }
216- else
217- {
218- // Multiple faulted tasks - return aggregate exception
219- var exceptions = faultedTasks . Select ( ft => ft . Exception ) . ToArray ( ) ;
220- var aggregateException = new AggregateException ( "Multiple tasks failed during archive operation" , exceptions ) ;
221- var faultedTaskNames = string . Join ( ", " , faultedTasks . Select ( ft => ft . Name ) ) ;
222- logger . LogError ( aggregateException , "Tasks failed: {FaultedTaskNames}" , faultedTaskNames ) ;
223- return Result . Fail ( $ "Archive operation failed: { faultedTaskNames } tasks failed") . WithError ( new ExceptionalError ( aggregateException ) ) ;
224- }
240+ logger . LogError ( e , "Unhandled exception" ) ;
241+ return Result . Fail ( $ "Archive operation failed: { e } ") . WithError ( new ExceptionalError ( e ) ) ;
225242 }
226243 }
227244
0 commit comments