Implement comprehensive 3-layer progress tracking system

ThreadSafeProgressCollector utility:
   Thread-safe progress data collection from background threads
   Concurrent queues for progress updates and verbose messages
   Safe PowerShell method calls only from main thread
   Automatic completion handling and queue processing

 MultiLayerProgressReporter for 3-layer progress:
   Layer 1: Collection progress (overall files with ETA)
   Layer 2: File progress (current file with size info)
   Layer 3: Transfer progress (bytes with speed calculation)
   Hierarchical progress reporting with parent-child relationships
   Real-time speed and ETA calculations

 Enhanced New-MinIOObject cmdlet integration:
   UploadSingleFileWithProgress method with progress integration
   Multi-file upload with comprehensive progress tracking
   Background thread safety for progress callbacks
   Verbose message queuing and processing
   Graceful error handling with progress completion

 Progress tracking features:
   Real-time transfer speed calculation
   Estimated time remaining (ETA) for collections
   Hierarchical progress bars in PowerShell
   Thread-safe verbose logging
   Automatic progress completion on success/failure

 Architecture improvements:
   Separation of concerns: progress collection vs reporting
   Background thread compatibility
   Memory-efficient concurrent collections
   Comprehensive error handling and cleanup

This provides enterprise-grade progress tracking for large file operations with
detailed visibility into collection, file, and transfer-level progress.
This commit is contained in:
PSMinIO Developer
2025-07-11 17:35:17 -04:00
parent e94cb35c0c
commit 6f693d7236
3 changed files with 454 additions and 24 deletions
+155
View File
@@ -0,0 +1,155 @@
using System;
using System.Collections.Concurrent;
using System.Management.Automation;
using System.Threading;
namespace PSMinIO.Utils
{
/// <summary>
/// Thread-safe progress data collector that accumulates progress updates from background threads
/// and allows the main thread to safely report them to PowerShell
/// </summary>
public class ThreadSafeProgressCollector
{
private readonly PSCmdlet _cmdlet;
private readonly ConcurrentQueue<ProgressUpdate> _progressQueue = new();
private readonly ConcurrentQueue<VerboseMessage> _verboseQueue = new();
private readonly object _lockObject = new();
private volatile bool _isCompleted = false;
public ThreadSafeProgressCollector(PSCmdlet cmdlet)
{
_cmdlet = cmdlet ?? throw new ArgumentNullException(nameof(cmdlet));
}
/// <summary>
/// Queues a progress update from a background thread
/// </summary>
public void QueueProgressUpdate(int activityId, string activity, string statusDescription, int percentComplete, int parentActivityId = -1)
{
if (_isCompleted) return;
_progressQueue.Enqueue(new ProgressUpdate
{
ActivityId = activityId,
Activity = activity,
StatusDescription = statusDescription,
PercentComplete = percentComplete,
ParentActivityId = parentActivityId,
Timestamp = DateTime.UtcNow
});
}
/// <summary>
/// Queues a progress completion from a background thread
/// </summary>
public void QueueProgressCompletion(int activityId, string activity, int parentActivityId = -1)
{
if (_isCompleted) return;
_progressQueue.Enqueue(new ProgressUpdate
{
ActivityId = activityId,
Activity = activity,
StatusDescription = "Completed",
PercentComplete = 100,
ParentActivityId = parentActivityId,
IsCompleted = true,
Timestamp = DateTime.UtcNow
});
}
/// <summary>
/// Queues a verbose message from a background thread
/// </summary>
public void QueueVerboseMessage(string message, params object[] args)
{
if (_isCompleted) return;
_verboseQueue.Enqueue(new VerboseMessage
{
Message = args.Length > 0 ? string.Format(message, args) : message,
Timestamp = DateTime.UtcNow
});
}
/// <summary>
/// Processes all queued updates from the main thread (safe to call PowerShell methods)
/// </summary>
public void ProcessQueuedUpdates()
{
// Process verbose messages first
while (_verboseQueue.TryDequeue(out var verboseMessage))
{
MinIOLogger.WriteVerbose(_cmdlet, verboseMessage.Message);
}
// Process progress updates
while (_progressQueue.TryDequeue(out var progressUpdate))
{
var progressRecord = new ProgressRecord(
progressUpdate.ActivityId,
progressUpdate.Activity,
progressUpdate.StatusDescription)
{
PercentComplete = progressUpdate.PercentComplete
};
if (progressUpdate.ParentActivityId >= 0)
{
progressRecord.ParentActivityId = progressUpdate.ParentActivityId;
}
if (progressUpdate.IsCompleted)
{
progressRecord.RecordType = ProgressRecordType.Completed;
}
_cmdlet.WriteProgress(progressRecord);
}
}
/// <summary>
/// Marks the collector as completed (no more updates will be accepted)
/// </summary>
public void Complete()
{
_isCompleted = true;
// Process any remaining updates
ProcessQueuedUpdates();
}
/// <summary>
/// Gets the number of pending progress updates
/// </summary>
public int PendingProgressUpdates => _progressQueue.Count;
/// <summary>
/// Gets the number of pending verbose messages
/// </summary>
public int PendingVerboseMessages => _verboseQueue.Count;
/// <summary>
/// Progress update data structure
/// </summary>
private class ProgressUpdate
{
public int ActivityId { get; set; }
public string Activity { get; set; } = string.Empty;
public string StatusDescription { get; set; } = string.Empty;
public int PercentComplete { get; set; }
public int ParentActivityId { get; set; } = -1;
public bool IsCompleted { get; set; }
public DateTime Timestamp { get; set; }
}
/// <summary>
/// Verbose message data structure
/// </summary>
private class VerboseMessage
{
public string Message { get; set; } = string.Empty;
public DateTime Timestamp { get; set; }
}
}
}