// Copyright (c) Microsoft. All rights reserved. using System; using System.Collections.Generic; using System.Threading.Channels; using System.Threading.Tasks; using Microsoft.Agents.AI.Purview.Models.Jobs; using Microsoft.Extensions.Logging; namespace Microsoft.Agents.AI.Purview; /// /// Handler class for background job management. /// internal class ChannelHandler : IChannelHandler { private readonly Channel _jobChannel; private readonly List _channelListeners; private readonly ILogger _logger; private readonly PurviewSettings _purviewSettings; /// /// Creates a new instance of JobHandler. /// /// The purview integration settings. /// The logger used for logging job information. /// The job channel used for queuing and reading background jobs. public ChannelHandler(PurviewSettings purviewSettings, ILogger logger, Channel jobChannel) { this._purviewSettings = purviewSettings; this._logger = logger; this._jobChannel = jobChannel; this._channelListeners = new List(this._purviewSettings.MaxConcurrentJobConsumers); } /// public void QueueJob(BackgroundJobBase job) { try { if (job == null) { throw new PurviewJobException("Cannot queue null job."); } if (this._channelListeners.Count == 0) { this._logger.LogWarning("No listeners are available to process the job."); throw new PurviewJobException("No listeners are available to process the job."); } bool canQueue = this._jobChannel.Writer.TryWrite(job); if (!canQueue) { int jobCount = this._jobChannel.Reader.Count; this._logger.LogError("Could not queue a job for background processing."); if (this._jobChannel.Reader.Completion.IsCompleted) { throw new PurviewJobException("Job channel is closed or completed. Cannot queue job."); } else if (jobCount >= this._purviewSettings.PendingBackgroundJobLimit) { throw new PurviewJobLimitExceededException($"Job queue is full. Current pending jobs: {jobCount}. Maximum number of queued jobs: {this._purviewSettings.PendingBackgroundJobLimit}"); } else { throw new PurviewJobException("Could not queue job for background processing."); } } } catch (Exception e) when (this._purviewSettings.IgnoreExceptions) { if (this._logger.IsEnabled(LogLevel.Error)) { this._logger.LogError(e, "Error queuing job: {ExceptionMessage}", e.Message); } } } /// public void AddRunner(Func, Task> runnerTask) { this._channelListeners.Add(Task.Run(async () => await runnerTask(this._jobChannel).ConfigureAwait(false))); } /// public async Task StopAndWaitForCompletionAsync() { this._jobChannel.Writer.Complete(); await this._jobChannel.Reader.Completion.ConfigureAwait(false); await Task.WhenAll(this._channelListeners).ConfigureAwait(false); } }