Skip to content

Hangfire Search - #2414

Open
fairking wants to merge 4 commits into
HangfireIO:mainfrom
fairking:Hangfire_Search
Open

fairking wants to merge 4 commits into
HangfireIO:mainfrom
fairking:Hangfire_Search

Conversation

@fairking

@fairking fairking commented Jun 6, 2024

Copy link
Copy Markdown

Introducing search in Hangfire Dashboard.

Minimal changes, but PLEASE BE AWARE there are breaking changes (see IMonitoringApi.cs) which I cannot see how we can avoid. On the other hand the search implementation could be very easy task for other projects derived from IMonitoringApi.

Search works as "Contains" and applies to the following fields:

  • Job InvocationData (class or method name),
  • Job Arguments (eg. can search userId or tenantId or any other data passed as arguments),

Search is available in:

  • Deleted Jobs,
  • Failed Jobs,
  • Processing Jobs,
  • Scheduled Jobs,
  • Succeeded Jobs.

Note:

  • Search does not work against State (exception messages or time executed). Could be improved later. But I found it less important than the search by method name or arguments.
  • Could take time to search if the number of jobs is huge. But it does not impact any performance if search is not involved.

Flexibility:
The search field which introduced in this PR could allow us to customize the search abilities in future. For example we can introduce some kind of language which would allow to search by multiple fields. Or use third party extensions which would do that. Or add such improvements in PRO version. Similar how github search field works. Eg:

  • MyJob // Search in job InvocationData only,
  • arg:9c5b94b1-35ad-49bb-b118-8e8fc24abf80 // Search in job Arguments by entity ID,
  • state:NullReferenceException // Search in state data by exception,
  • date:2024-01-01 // Search by state date,
  • param:RecurringJobId="easy_job1" // Search by job parameter
  • param:Tenant="ABC" // Another search by job parameter
  • MyJob,state:NullReferenceException,date:2024-01-01 // Search by multiple fields,
  • MyJob,param:Tenant="ABC" // Another search by multiple fields,
  • etc..

Closes #2287
Closes #2253
Closes #717

Screenshots:

image

image

@odinserj

odinserj commented Jun 7, 2024

Copy link
Copy Markdown
Member

Hello @fairking! The feature is really nice and looks awesome, but it can't be merged with Hangfire, because of breaking changes. A lot of packages, storages depend on this API, and we can't break everything. So the only viable option I see currently is to think how to make this as an extension package for Hangfire, but unfortunately I can't guide you this way, because it's not a trivial thing.

@fairking

fairking commented Jun 7, 2024

Copy link
Copy Markdown
Author

Hello @fairking! The feature is really nice and looks awesome, but it can't be merged with Hangfire, because of breaking changes. A lot of packages, storages depend on this API, and we can't break everything. So the only viable option I see currently is to think how to make this as an extension package for Hangfire, but unfortunately I can't guide you this way, because it's not a trivial thing.

Thanks @odinserj for you honest opinion. Of course I would do it as an extension if it was possible. But I don't want to waste time by creating some crazy workarounds. I would like everything to be simple and straight forward.

I see the following options:

  • Ask other packages for their opinion whether they are happy with such breaking change,
  • Include it into the next Minor version (eg. v.1.9.0),
  • Modify the JobStorageMonitor so it can be customizable by extensions,
  • Wait for the Major version v.2.0.0 :-(

@montyclt

Copy link
Copy Markdown

What about just adding an overload with and without search parameter in src/Hangfire.Core/Storage/IMonitoringApi.cs?

That would solve the breaking changes.

@acandylevey

Copy link
Copy Markdown

What about just adding an overload with and without search parameter in src/Hangfire.Core/Storage/IMonitoringApi.cs?

That would solve the breaking changes.

I think this would be the most obvious solution - If you wanted to remove entirely for v2.0.0 The existing calls could be marked as Obsolete to give packages time to convert before that happens.

@frankyjquintero

Copy link
Copy Markdown

@fairking
I think we could approach this functionality without introducing breaking changes into the current API.

Instead of extending or modifying IMonitoringApi and the existing methods directly, an alternative would be to completely isolate the search and filtering logic as a new system capability. The idea would be to introduce a new tab or section dedicated exclusively to advanced search within the dashboard, decoupled from the existing monitoring views.

From a technical perspective, this could be implemented through:

Extension methods for JobStorage

Extension methods for the monitoring API

An optional feature/capability that each storage implementation may support or not

With this approach:

Existing contracts remain untouched

There is no need to modify current methods or add additional parameters to already public APIs

Storage providers can adopt the feature progressively

The UI can enable or disable the experience depending on the capabilities supported by the storage

Additionally, this model opens the door to much more flexible scenarios than a simple Contains search over jobs. For example:

Search by states

Search by specific fields

Search by method or type name

Search over arguments

Chained filters

Advanced query syntax in the future

Essentially, this would behave more like a dedicated search engine layer, separated from the traditional monitoring APIs.

The main benefit is that search would no longer be a side effect embedded into existing monitoring APIs, but instead become an independent, extensible, and evolvable feature.

This could even be exposed through a capability validation mechanism (for example, SupportsAdvancedSearch), allowing the core to automatically enable the functionality only when the underlying storage supports it.

With this approach, we could introduce this capability into the Hangfire core without introducing breaking changes or impacting existing IMonitoringApi implementations.

@frankyjquintero

frankyjquintero commented May 21, 2026

Copy link
Copy Markdown

If someone is willing to implement the required tests to validate the integration of these two new extension methods for advanced job search, I’d be open to moving forward with the PR and submitting the changes.

For now, the idea is to keep it as a transparent and fully configurable feature, exposed as a new capability supported by JobStorage on top of the monitoring API.

It would even be possible to add support for JSON_VALUE and create indexes by detecting the database engine version, dynamically generating the query for each type of filter based on that information.

@odinserj Do you see this possibility as viable?

image

Examples changes:

using System;
using System.Collections.Generic;
using Hangfire.Storage.Monitoring;

namespace Hangfire.Storage
{
    public interface IMonitoringApi
    {
        IList<QueueWithTopEnqueuedJobsDto> Queues();
        IList<ServerDto> Servers();
        JobDetailsDto JobDetails(string jobId);
        StatisticsDto GetStatistics();

        JobList<EnqueuedJobDto> EnqueuedJobs(string queue, int from, int perPage);
        JobList<FetchedJobDto> FetchedJobs(string queue, int from, int perPage);

        JobList<ProcessingJobDto> ProcessingJobs(int from, int count);
        JobList<ScheduledJobDto> ScheduledJobs(int from, int count);
        JobList<SucceededJobDto> SucceededJobs(int from, int count);
        JobList<FailedJobDto> FailedJobs(int from, int count);
        JobList<DeletedJobDto> DeletedJobs(int from, int count);

        long ScheduledCount();
        long EnqueuedCount(string queue);
        long FetchedCount(string queue);
        long FailedCount();
        long ProcessingCount();

        long SucceededListCount();
        long DeletedListCount();
        
        IDictionary<DateTime, long> SucceededByDatesCount();
        IDictionary<DateTime, long> FailedByDatesCount();
        IDictionary<DateTime, long> HourlySucceededJobs();
        IDictionary<DateTime, long> HourlyFailedJobs();

        JobList<JobSearchResultDto> SearchJobs(SearchJobsRequest request, int from, int count);
        long SearchJobsCount(SearchJobsRequest request);
    }
}

        public static class Monitoring
        {
            public static readonly string DeletedStateGraphs = "Monitoring.DeletedStateGraphs";
            public static readonly string AwaitingJobs = "Monitoring.AwaitingJobs";
            public static readonly string AdvancedSearch = "Monitoring.AdvancedSearch";
        }
image

@frankyjquintero

Copy link
Copy Markdown

It would also be necessary to add dark mode support and provide localization support for field translations.

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Development

Successfully merging this pull request may close these issues.

Ability to search in Suceedeed jobs Dashboard recurring jobs: ability to search Search / filter jobs viewed in Dashboard

5 participants