Skip to content

feat: Add Supervisor UI component to view timeline of rules applied by a cascading reindexing supervisor#19051

Open
capistrant wants to merge 29 commits into
apache:masterfrom
capistrant:reindexing-timeline-ui
Open

feat: Add Supervisor UI component to view timeline of rules applied by a cascading reindexing supervisor#19051
capistrant wants to merge 29 commits into
apache:masterfrom
capistrant:reindexing-timeline-ui

Conversation

@capistrant
Copy link
Copy Markdown
Contributor

@capistrant capistrant commented Feb 25, 2026

Description

tied to the effort in #19092 to productionalize this new feature

New Supervisor API (cascading reindexing supervisors only)

GET /druid/indexer/v1/supervisor/{supervisorId}/reindexingTimeline

This generates a timeline of search intervals for reindexing with the effective sets of rules that are used to create the underlying inline compaction configs for the different intervals. This is the business end of the console UI described below

Console visualization of reindexing timeline (cascading reindexing supervisors only)

For cascading compaction supervisors you can get a timeline view of how rules from the rule provider(s) will apply to your datasource. You can view how skip offsets (if they are configured) will change the timeline too. This visualization is a great help for operators in understanding in how their underlying rules will be applied across the datasource history. I will add some screen shots for reference

image image image image

Refactor CompactionSupervisorTest Embedded Tests

Split out the cascading template tests to their own class from the inline compaction supervisor tests. Sharing one file was getting convoluted. They share a base class for what makes sense.

Try it Yourself

If you build this branch locally and start up a cluster. post the following spec example to supervisors api and you can view a prospective timeline plus play with the config if you want to see how it can change. note that you do not have to pre-ingest the wikipedia data! this supervisor will be suspended and has no reliance on underlying data (caveat is that if you try to do skipOffsetFromLatest and click the dynamic button to calculate, you would need to have the data ingested)

Click to expand code
{
"type": "autocompact",
"spec": {
  "type": "reindexCascade",
  "dataSource": "wikipedia",
  "taskPriority": 25,
  "inputSegmentSizeBytes": 100000000000000,
  "ruleProvider": {
    "type": "inline",
    "deletionRules": [
      {
        "id": "delete_japan",
        "description": null,
        "olderThan": "P90D",
        "deleteWhere": {
          "type": "equals",
          "column": "countryName",
          "matchValueType": "STRING",
          "matchValue": "Japan"
        },
        "virtualColumns": null
      }
    ],
    "partitioningRules": [],
    "indexSpecRules": [],
    "dataSchemaRules": [
      {
        "id": "dataSchema-1",
        "description": null,
        "olderThan": "P30D",
        "dimensionsSpec": {
          "dimensions": null
        },
        "metricsSpec": [],
        "queryGranularity": "HOUR",
        "rollup": true,
        "projections": []
      }
    ]
  },
  "taskContext": {
    "useConcurrentLocks": true,
    "maxNumTasks": 2
  },
  "skipOffsetFromLatest": null,
  "skipOffsetFromNow": null,
  "defaultSegmentGranularity": "DAY",
  "defaultPartitionsSpec": {
    "type": "range",
    "targetRowsPerSegment": 5,
    "maxRowsPerSegment": null,
    "partitionDimensions": [
      "city_and_country",
      "isRobot"
    ],
    "assumeGrouped": false
  },
  "defaultPartitioningVirtualColumns": [
    {
      "type": "expression",
      "name": "city_and_country",
      "expression": "concat(cityName, ':', countryName)",
      "outputType": "STRING"
    }
  ],
  "tuningConfig": null
},
"suspended": true
}

Release note


Key changed/added classes in this PR
  • CascadingReindexingTemplate
  • ReindexingTimelineView
  • SupervisorResource
  • web-console/src/components/reindexing-timeline/reindexing-timeline.scss

This PR has:

  • been self-reviewed.
  • added documentation for new or modified features or behaviors.
  • a release note entry in the PR description.
  • added Javadocs for most classes and all non-trivial methods. Linked related entities via Javadoc links.
  • added or updated version, license, or notice information in licenses.yaml
  • added comments explaining the "why" and the intent of the code wherever would not be obvious for an unfamiliar reader.
  • added unit tests or modified existing tests to cover new code paths, ensuring the threshold for code coverage is met.
  • added integration tests.
  • been tested in a test Druid cluster.

@capistrant capistrant changed the title Reindexing timeline UI Cascading Reindexing Supervisor UI component fore viewing timeline of rule applications Feb 25, 2026
Copy link
Copy Markdown
Contributor

@kfaraz kfaraz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pretty cool!
Left some minor suggestions.

Will try this out locally.
Would also be nice to get a review from @vogievetsky on the web-console stuff.

Comment on lines +234 to +235
private final AppliedSkipOffset applied;
private final NotAppliedSkipOffset notApplied;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: These two classes feel a little unnecessary. We could have just have the actual fields in the class SkipOffsetInfo directly: type, period, a boolean for isApplied, a nullable effectiveEndTime mutually exclusive with a nullable errorMessage.

"tuning-7d",
null,
Period.days(7),
new UserCompactionTaskQueryTuningConfig(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Didn't we add a builder for this class in the previous PR?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no, it is coming in #18996 though

Comment on lines +1731 to +1733
/**
* Test validation error when granularity timeline is invalid
*/
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really needed since the test name captures this already.

Comment on lines +1680 to +1682
/**
* Test that skipOffsetFromNow correctly skips intervals and populates skipOffset.applied
*/
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really needed as the test name already captures this.

// Older data (P90D) has DAY granularity, newer data (P30D) has HOUR granularity
// This means as we move from past to present, granularity gets finer (valid)
// But then if we add MONTH for recent data, it becomes coarser (invalid)
ReindexingRuleProvider provider = InlineReindexingRuleProvider.builder()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Style tip: Move the .builder() to the next line to make the whole thing feel less crowded.

Suggested change
ReindexingRuleProvider provider = InlineReindexingRuleProvider.builder()
ReindexingRuleProvider provider = InlineReindexingRuleProvider
.builder()

* @param referenceTime the reference time to use for computing rule periods (typically DateTime.now())
* @return a view of the reindexing timeline with intervals and their configs
*/
public ReindexingTimelineView getReindexingTimelineView(DateTime referenceTime)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like this method should not live here as it kind of pollutes the template definition.

Either have the timeline be built using a static method like ReindexingTimelineView.fromTemplate(template, referenceTime) or have a separate builder class altogether.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm. I wonder about how we'd manage to re-use a lot of the underlying private implementation details that are quite convoluted (unfortunately). Like creating the base timeline from segment gran rules and what not is all shared between the timeline creation and the task creation. I agree that it feels a little weird here, but I didn't know how else to approach it without either duping a lot of internal logic or exposing it to both somehow. will keep thinking though

@kfaraz kfaraz changed the title Cascading Reindexing Supervisor UI component fore viewing timeline of rule applications Cascading Reindexing Supervisor UI component for viewing timeline of rule applications Feb 25, 2026
@kfaraz kfaraz changed the title Cascading Reindexing Supervisor UI component for viewing timeline of rule applications Supervisor UI component to view timeline of rules applied for a cascading reindexing supervisor Feb 25, 2026
@kfaraz
Copy link
Copy Markdown
Contributor

kfaraz commented Feb 25, 2026

@capistrant , I took the liberty to slightly update the title for clarity. Hope it seems less ambiguous now.

@kfaraz kfaraz changed the title Supervisor UI component to view timeline of rules applied for a cascading reindexing supervisor Add Supervisor UI component to view timeline of rules applied by a cascading reindexing supervisor Feb 25, 2026
@kfaraz
Copy link
Copy Markdown
Contributor

kfaraz commented Feb 25, 2026

Was trying out the embedded test in this PR to check out the timeline.

@capistrant , in the snapshot below, what does the "4 rules applied" mean?
The widget below it shows only 2 items - a segment granularity and a deletion rule.

Maybe we should include an info popup for each of the chips.
Also, the chip says `"1" deletion rule". When can there be more than 1 for a single interval?

Screenshot 2026-02-25 at 12 50 41 PM

@capistrant
Copy link
Copy Markdown
Contributor Author

Was trying out the embedded test in this PR to check out the timeline.

@capistrant , in the snapshot below, what does the "4 rules applied" mean? The widget below it shows only 2 items - a segment granularity and a deletion rule.

Maybe we should include an info popup for each of the chips. Also, the chip says `"1" deletion rule". When can there be more than 1 for a single interval?

Screenshot 2026-02-25 at 12 50 41 PM

4 rules applied means that the config created for the interval is made up of 4 different reindexing rules. In this case based on the test I assume you are running, it is

  • segmentGran rule
  • deletion rule
  • ioConfig rule
  • tuningConfig rule

You could confirm by clicking the view raw rules button.

I think this is a UX issue. Hand up, I don't have any experience curating a good UX :). If I had to guess what UX decision is best here, it is to remove the N rules applied chip. if someone really wants to see the explicit underlying rules they can click view raw rules.

As for deletion rules, N deletion rules can apply to any given interval. They accumulate (unlike other rule types). Every deletion rule that applies a search interval will be combined together.

  • deleteWhere "countryName=france" olderThan P7D"
  • deleteWhere "countryName=italy" olderThan P14D"

Any interval where both of those apply would have 2 deletion rules.

The chips are intended to give very quick reference to what is in the config for the given search interval. They are not strictly tied to rules even. In my pictures you see metrics and dims in separate chips, but both belong to the same rule. So maybe the deletion rules chip should actually be like 2 deletion filters or 2 deletion clauses. I didn't know how valuable chips would be for tuningConfig and ioConfig so those are not surfaced, likely leading to your initial confusion. You'd have to view the config payload or the raw rules to see them.

@capistrant
Copy link
Copy Markdown
Contributor Author

Was trying out the embedded test in this PR to check out the timeline.
@capistrant , in the snapshot below, what does the "4 rules applied" mean? The widget below it shows only 2 items - a segment granularity and a deletion rule.
Maybe we should include an info popup for each of the chips. Also, the chip says `"1" deletion rule". When can there be more than 1 for a single interval?
Screenshot 2026-02-25 at 12 50 41 PM

4 rules applied means that the config created for the interval is made up of 4 different reindexing rules. In this case based on the test I assume you are running, it is

  • segmentGran rule
  • deletion rule
  • ioConfig rule
  • tuningConfig rule

You could confirm by clicking the view raw rules button.

I think this is a UX issue. Hand up, I don't have any experience curating a good UX :). If I had to guess what UX decision is best here, it is to remove the N rules applied chip. if someone really wants to see the explicit underlying rules they can click view raw rules.

As for deletion rules, N deletion rules can apply to any given interval. They accumulate (unlike other rule types). Every deletion rule that applies a search interval will be combined together.

  • deleteWhere "countryName=france" olderThan P7D"
  • deleteWhere "countryName=italy" olderThan P14D"

Any interval where both of those apply would have 2 deletion rules.

The chips are intended to give very quick reference to what is in the config for the given search interval. They are not strictly tied to rules even. In my pictures you see metrics and dims in separate chips, but both belong to the same rule. So maybe the deletion rules chip should actually be like 2 deletion filters or 2 deletion clauses. I didn't know how valuable chips would be for tuningConfig and ioConfig so those are not surfaced, likely leading to your initial confusion. You'd have to view the config payload or the raw rules to see them.

I made some minor UX updates that I think help clear up some of your initial confusion. I updated the description images to reflect (2nd and 3rd image)

  1. no more N rules applied chip. Instead updated the raw rules button to say how many rules there are applied.
  2. removed the word "rule" from the deletion chip. I don't want folks to be confused into thinking that executive summary of the interval are 1:1 with rules.
  3. In that same summary row of chips I added a label "Effective Configuration" which has a tooltip to tell the operator that it is a summary and the full config can be referenced via the button(s) below.
  4. Added a partitioning type chip as that may be interesting.

function getPartitionType(tuningConfig: Record<string, unknown> | undefined): string | undefined {
if (!tuningConfig) return undefined;
const partitionsSpec = tuningConfig.partitionsSpec;
if (partitionsSpec && typeof partitionsSpec === 'object' && partitionsSpec !== null) {

Check warning

Code scanning / CodeQL

Comparison between inconvertible types Warning

Variable 'partitionsSpec' is of type date, object or regular expression, but it is compared to
an expression
of type null.
@kfaraz
Copy link
Copy Markdown
Contributor

kfaraz commented Mar 11, 2026

@capistrant , is this PR ready for another round of review or should we hold off until the refactor PR #19106 is reviewed and merged?

@capistrant
Copy link
Copy Markdown
Contributor Author

@capistrant , is this PR ready for another round of review or should we hold off until the refactor PR #19106 is reviewed and merged?

Can probably hold off for now until the rule refactor is done

@capistrant capistrant marked this pull request as draft March 16, 2026 21:59
@capistrant capistrant marked this pull request as ready for review March 27, 2026 15:12
@capistrant capistrant changed the title Add Supervisor UI component to view timeline of rules applied by a cascading reindexing supervisor feat: Add Supervisor UI component to view timeline of rules applied by a cascading reindexing supervisor Mar 27, 2026
searchInterval,
referenceTime,
searchIntervals,
null
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Timeline preview drops template tuningConfig

getReindexingTimelineView builds each interval config with a ReindexingConfigBuilder whose baseTuningConfig is null, while createCompactionJobs passes the template's tuningConfig into the same builder. Any cascading supervisor with static tuning settings such as maxRowsInMemory, splitHintSpec, maxNumConcurrentSubTasks, indexSpecForIntermediatePersists, or retry/chat settings will have those settings in the actual compaction jobs but not in the new reindexingTimeline API response or downloaded UI config. This makes the preview/API incompatible with the real effective config. Pass tuningConfig here as createCompactionJobs does, and add a test that a non-partitionsSpec tuning field survives in the timeline view.

Copy link
Copy Markdown
Member

@FrankChen021 FrankChen021 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Severity Findings
P0 0
P1 0
P2 1
P3 0
Total 1

Reviewed 16 of 16 changed files.


This is an automated review by Codex GPT-5.5

Interval searchInterval = intervalInfo.getInterval();

// Check if interval extends past skip offset
if (intervalEndsAfter(searchInterval, effectiveEndTime)) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Timeline marks partially compacted intervals as skipped

createCompactionJobs now truncates an interval that crosses the skip-offset boundary and still creates jobs for the older aligned subrange, but getReindexingTimelineView still returns ruleCount 0/config null for any interval whose end is after effectiveEndTime. For skipOffsetFromNow, and for the console's skipOffsetFromLatest preview after querying maxTime, the UI will show an interval as fully skipped even though the supervisor will compact part of it. Please mirror the truncation/splitting behavior in the timeline view so the preview matches actual job generation.

Copy link
Copy Markdown
Member

@FrankChen021 FrankChen021 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Severity Findings
P0 0
P1 0
P2 1
P3 1
Total 2

Reviewed 16 of 16 changed files.


This is an automated review by Codex GPT-5.5

queryType: 'timeBoundary',
dataSource: timelineData.dataSource,
};
const resp = await Api.instance.post('/druid/v2', query);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Compute skipOffsetFromLatest from the supervisor timeline

The Query latest timestamp path bases the preview boundary on a broker timeBoundary maxTime, then subtracts the period in the browser. The actual cascading supervisor path applies skipOffsetFromLatest to the segment timeline end and then truncates to an interval granularity-aligned boundary, so row maxTime can be earlier than the segment end and straddling intervals are handled differently. For normal DAY/HOUR segments this can show intervals as skipped or active at the wrong boundary. Please derive the preview from segment metadata/server-side timeline logic, or mirror the same segment-end and granularity-alignment rules.

active: activeTab === 'history',
onClick: () => setActiveTab('history'),
},
...(isCompactionSupervisor
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P3] Hide the timeline tab for non-cascading compaction supervisors

This adds the Timeline tab for every supervisor ID that starts with autocompact__, but the new endpoint returns 400 unless the compaction supervisor template is CascadingReindexingTemplate. Existing inline-config compaction supervisors also use autocompact__ IDs, so standard compaction supervisors now expose a tab that only shows an error. Gate the tab on the loaded supervisor spec/template type, or render a deliberate unavailable state instead of adding the tab solely from the ID prefix.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants