Skip to content

DatasetSliceTrack - Dynamic Datasets #1638

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion ui/src/components/tracks/base_slice_track.ts
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,12 @@ export abstract class BaseSliceTrack<
return `slice_${this.trackUuid}`;
}

async onCreate(): Promise<void> {
private oldQuery?: string;

private async initialize(): Promise<void> {
// This disposes all already initialized stuff and empties the trash.
await this.trash.asyncDispose();

const result = await this.onInit();
result && this.trash.use(result);

Expand Down Expand Up @@ -390,10 +395,18 @@ export abstract class BaseSliceTrack<

this.trash.defer(async () => {
await this.engine.tryQuery(`drop table ${this.getTableName()}`);
this.oldQuery = undefined;
this.slicesKey = CacheKey.zero();
});
}

async onUpdate({visibleWindow, size}: TrackRenderContext): Promise<void> {
const query = this.getSqlSource();
if (query !== this.oldQuery) {
await this.initialize();
this.oldQuery = query;
}

const windowSizePx = Math.max(1, size.width);
const timespan = visibleWindow.toTimeSpan();
const rawSlicesKey = CacheKey.create(
Expand Down
38 changes: 20 additions & 18 deletions ui/src/components/tracks/dataset_slice_track.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@

import m from 'mithril';
import {ColorScheme} from '../../base/color_scheme';
import {assertTrue} from '../../base/logging';
import {Time} from '../../base/time';
import {TrackEventDetailsPanel} from '../../public/details_panel';
import {TrackEventDetails, TrackEventSelection} from '../../public/selection';
Expand Down Expand Up @@ -96,7 +95,7 @@ export interface DatasetSliceTrackAttrs<T extends DatasetSchema> {
* different layers will be mipmapped independency of each other, and the
* buckets of higher layers will be rendered on top of lower layers.
*/
readonly dataset: SourceDataset<T>;
readonly dataset: SourceDataset<T> | (() => SourceDataset<T>);

/**
* An optional initial estimate for the maximum depth value. Helps minimize
Expand Down Expand Up @@ -182,41 +181,39 @@ export type ROW_SCHEMA = typeof rowSchema;
// resolved properly.
type SliceWithRow<T> = Slice & {row: T};

function getDataset<T extends DatasetSchema>(
attrs: DatasetSliceTrackAttrs<T>,
): SourceDataset<T> {
const dataset = attrs.dataset;
return typeof dataset === 'function' ? dataset() : dataset;
}

export class DatasetSliceTrack<T extends ROW_SCHEMA> extends BaseSliceTrack<
SliceWithRow<T>,
BaseRow & T
> {
protected readonly sqlSource: string;
readonly rootTableName?: string;

constructor(private readonly attrs: DatasetSliceTrackAttrs<T>) {
const dataset = getDataset(attrs);
super(
attrs.trace,
attrs.uri,
{...BASE_ROW, ...attrs.dataset.schema},
{...BASE_ROW, ...dataset.schema},
attrs.sliceLayout,
attrs.initialMaxDepth,
attrs.instantStyle?.width,
);
const {dataset} = attrs;

// This is the minimum viable implementation that the source dataset must
// implement for the track to work properly. Typescript should enforce this
// now, but typescript can be worked around, and checking it is cheap.
// Better to error out early.
assertTrue(this.attrs.dataset.implements(rowSchema));

this.sqlSource = this.generateRenderQuery(dataset);
this.rootTableName = attrs.rootTableName;
}

override rowToSlice(row: BaseRow & T): SliceWithRow<T> {
const slice = this.rowToSliceBase(row);
const title = this.getTitle(row);
const color = this.getColor(row, title);

const dataset = getDataset(this.attrs);
// Take a copy of the row, only copying the keys listed in the schema.
const cols = Object.keys(this.attrs.dataset.schema);
const cols = Object.keys(dataset.schema);
const clonedRow = Object.fromEntries(
Object.entries(row).filter(([key]) => cols.includes(key)),
) as T;
Expand Down Expand Up @@ -276,11 +273,15 @@ export class DatasetSliceTrack<T extends ROW_SCHEMA> extends BaseSliceTrack<
}

override getSqlSource(): string {
return this.sqlSource;
const dataset =
typeof this.attrs.dataset === 'function'
? this.attrs.dataset()
: this.attrs.dataset;
return this.generateRenderQuery(dataset);
}

getDataset() {
return this.attrs.dataset;
return getDataset(this.attrs);
}

detailsPanel(sel: TrackEventSelection): TrackEventDetailsPanel | undefined {
Expand All @@ -301,7 +302,8 @@ export class DatasetSliceTrack<T extends ROW_SCHEMA> extends BaseSliceTrack<
async getSelectionDetails(
id: number,
): Promise<TrackEventDetails | undefined> {
const {trace, dataset} = this.attrs;
const {trace} = this.attrs;
const dataset = getDataset(this.attrs);
const result = await trace.engine.query(`
SELECT *
FROM (${dataset.query()})
Expand Down
Loading