Open Closed

Hangfire #7204


User avatar
0
oleksandra.nakonechniuk created

We are using MultiTenancy. When I created scheduled job and it was executed it doesn't work as expected. It doesn't have the correct context - TenantId is null. But when I was calling job Tenat was not null. Does it mean that hangfire jobs doesn't work with multiTenancy. Or if you can suggest some approach please write.

  • ABP Framework version: vX.X.X
  • UI Type: Angular
  • Database System: EF Core ( PostgreSQL, etc..)
    • Auth Server Separated (for Angular)**: yes
  • Exception message and full stack trace:
  • Steps to reproduce the issue:

4 Answer(s)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    When I created scheduled job and it was executed it doesn't work as expected. It doesn't have the correct context - TenantId is null.

    Please share your code.

    Thanks

  • User Avatar
    0
    oleksandra.nakonechniuk created
    public class AssessmentJobsService : DomainService, IAssessmentJobsService
    {
        #region Fields
    
        private readonly IAssessmentDetailRepository _assessmentDetailRepository;
        private readonly IMediaRepository _mediaRepository;
        private readonly IBlobManager _blobManager;
        private readonly IAssessmentRepository _assessmentRepository;
    
        #endregion
    
        #region Constructors
    
        /// <summary>
        /// Initializes a new instance of the <see cref="AssessmentJobsService" /> class.
        /// </summary>
        /// <param name="assessmentDetailRepository">The assessment repository.</param>
        /// <param name="mediaRepository">The media repository.</param>
        /// <param name="blobManager">The blob manager.</param>
        public AssessmentJobsService(
            IAssessmentDetailRepository assessmentDetailRepository,
            IMediaRepository mediaRepository,
            IBlobManager blobManager,
            IAssessmentRepository assessmentRepository)
        {
            _assessmentDetailRepository = assessmentDetailRepository;
            _mediaRepository = mediaRepository;
            _blobManager = blobManager;
            _assessmentRepository = assessmentRepository;
        }
    
        #endregion
    
        /// <inheritdoc/>
        public void SheduleAssessmentCleanup(long assessmentId)
        {
            BackgroundJob.Schedule(() => CleanUpAssessment(assessmentId), TimeSpan.FromSeconds(30));
        }
    
        public void SheduleEmptyAssessmentCleanup(long assessmentId)
        {
            BackgroundJob.Schedule(() => CleanUpEmptyAssessment(assessmentId), TimeSpan.FromHours(4));
        }
    
        /// <inheritdoc/>
        public void SheduleMediaCleanup(long mediaId)
        {
            BackgroundJob.Schedule(() => CleanUpMedia(mediaId), TimeSpan.FromHours(4));
        }
    
        /// <inheritdoc/>
        public async Task StartTrackingJob(long assessmentDetailId, bool isOneStepProcess)
        {
            // Create a Hangfire job to finish the job if not all methods are called within 5 minutes
            var jobId = BackgroundJob.Schedule(() => CheckProcessVideoTracking(assessmentDetailId, isOneStepProcess), TimeSpan.FromMinutes(3));
            var assessementDetails = await _assessmentDetailRepository.GetAsync(assessmentDetailId);
            assessementDetails.JobId = jobId;
    
            // Sets default status
            assessementDetails.ProcessVideoTrackStatus = (int)ProcessVideoTrackingStatus.InProgress;
            assessementDetails.ProcessVideoTrackStatusValue = ProcessVideoTrackingStatus.InProgress.ToString();
            await _assessmentDetailRepository.UpdateAsync(assessementDetails, true);
        }
    
        /// <inheritdoc/>
        //[AutomaticRetry(Attempts = 5, DelaysInSeconds = new[] { 300, 600, 900, 1800, 3600 })]
        [AutomaticRetry(Attempts = 5, DelaysInSeconds = new[] { 60, 60, 60 })]
        public async Task CheckProcessVideoTracking(long assessmentDetailId, bool isOneStepProcess)
        {
           var assessmentDetails = await _assessmentDetailRepository.GetAsync(assessmentDetailId)
                ?? throw new Exception($"Job failed. Assessment details were not found");
            var status = (ProcessVideoTrackingStatus)assessmentDetails.ProcessVideoTrackStatus;
    
            if ((isOneStepProcess && status == ProcessVideoTrackingStatus.ProcessData) ||
            (!isOneStepProcess && status == (ProcessVideoTrackingStatus.ProcessData | ProcessVideoTrackingStatus.Report | ProcessVideoTrackingStatus.Overlay)))
            {
                return;
            }
    
            throw new Exception($"Job failed. Status {status}");
        }
    
        /// <inheritdoc/>
        public async Task CleanUpMedia(long mediaId)
        {
            var mediaIsInUse = await _mediaRepository.CheckMediaWithAssessmentDetails(mediaId);
            if (!mediaIsInUse)
            {
                var media = await _mediaRepository.FindAsync(mediaId);
                if(media == null)
                {
                    return;
                }
                await _mediaRepository.DeleteAsync(mediaId);
                await _blobManager.DeleteBlob(media?.MediaUrl, media?.FileName);
            }
        }
    
        /// <inheritdoc/>
        public async Task CleanUpMediaMedBridge()
        {
            try
            {
                // Get details from assessment where externalAssessmentId != null
                var mediaIds = await _assessmentDetailRepository.GetListMediaIdAndCleanDetailsMedbridgeAsync();
                if (mediaIds != null)
                {
                    var mediaQueryable = await _mediaRepository.GetQueryableAsync();
                    var medias = mediaQueryable.Where(m => mediaIds.Contains(m.Id)).ToList();
                    if (medias == null)
                    {
                        return;
                    }
    
                    await _mediaRepository.DeleteManyAsync(mediaIds, true);
                    foreach (var media in medias)
                    {
                        await _blobManager.DeleteBlob(media?.MediaUrl, media?.FileName);
                    }
                }
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }
    
    
        /// <inheritdoc/>
        public async Task CleanUpAssessment(long assessmentId)
        {
            var details = await _assessmentDetailRepository.GetListAsync(x => x.AssessmentId == assessmentId);
            var mediaIds = details.Select(x => x.MediaId).ToList();
            await _assessmentDetailRepository.DeleteManyAsync(details);
    
            var medias = await _mediaRepository.GetListAsync(x => mediaIds.Contains(x.Id));
            await _mediaRepository.DeleteManyAsync(medias);
    
            foreach (var media in medias)
            {
                await _blobManager.DeleteBlob(media.MediaUrl, media.FileName);
            }
        }
    
        public async Task CleanUpEmptyAssessment(long assessmentId)
        {
            if (!await _assessmentDetailRepository.CheckAssessmentWithAssessmentDetails(assessmentId))
            {
                var assessment = await _assessmentRepository.FindAsync(assessmentId);
                if (assessment != null)
                {
                    await _assessmentRepository.DeleteAsync(assessment);
                }
            }
        }
    }
    
    

    and in HttpHostModule

     private void ConfigureHangfire(ServiceConfigurationContext context, IConfiguration configuration)
     {
         context.Services.AddHangfire(config =>
         {
             GlobalConfiguration.Configuration.UsePostgreSqlStorage(configuration.GetConnectionString("Default"));
         });
     }
    
  • User Avatar
    0
    oleksandra.nakonechniuk created

    And one moment I use API and we have API-Key authentication.

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    You can add a tenantid parameter to the methods.

    BackgroundJob.Schedule(() => CleanUpMediaMedBridge(tenantid), TimeSpan.FromHours(4));

    public async Task CleanUpMediaMedBridge(Guid? tenantid)
    {
       //https://docs.abp.io/en/abp/latest/Multi-Tenancy#change-the-current-tenant
    }
    

    The CleanUpMediaMedBridge method is called by hangfire. So it will lose all context.

Made with ❤️ on ABP v9.1.0-preview. Updated on November 11, 2024, 11:11