Skip to content

Commit aeddce7

Browse files
authored
DatasetSliceTrack - Dynamic Datasets (#1638)
This change modifies the interface to `DatasetSliceTrack`, allowing it to take either a static dataset or a dynamic dataset (a function that returns a dataset, called on the track's onUpdate() hook). This allows the dataset to be tweaked by the plugin that registers the track depending on some other internal state of the plugin. If the dataset's resultant query changes from frame to frame, the track's internal state and any tables are dropped, and the track data is reloaded.
1 parent dd35b29 commit aeddce7

File tree

2 files changed

+34
-19
lines changed

2 files changed

+34
-19
lines changed

ui/src/components/tracks/base_slice_track.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,12 @@ export abstract class BaseSliceTrack<
310310
return `slice_${this.trackUuid}`;
311311
}
312312

313-
async onCreate(): Promise<void> {
313+
private oldQuery?: string;
314+
315+
private async initialize(): Promise<void> {
316+
// This disposes all already initialized stuff and empties the trash.
317+
await this.trash.asyncDispose();
318+
314319
const result = await this.onInit();
315320
result && this.trash.use(result);
316321

@@ -390,10 +395,18 @@ export abstract class BaseSliceTrack<
390395

391396
this.trash.defer(async () => {
392397
await this.engine.tryQuery(`drop table ${this.getTableName()}`);
398+
this.oldQuery = undefined;
399+
this.slicesKey = CacheKey.zero();
393400
});
394401
}
395402

396403
async onUpdate({visibleWindow, size}: TrackRenderContext): Promise<void> {
404+
const query = this.getSqlSource();
405+
if (query !== this.oldQuery) {
406+
await this.initialize();
407+
this.oldQuery = query;
408+
}
409+
397410
const windowSizePx = Math.max(1, size.width);
398411
const timespan = visibleWindow.toTimeSpan();
399412
const rawSlicesKey = CacheKey.create(

ui/src/components/tracks/dataset_slice_track.ts

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414

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

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

184+
function getDataset<T extends DatasetSchema>(
185+
attrs: DatasetSliceTrackAttrs<T>,
186+
): SourceDataset<T> {
187+
const dataset = attrs.dataset;
188+
return typeof dataset === 'function' ? dataset() : dataset;
189+
}
190+
185191
export class DatasetSliceTrack<T extends ROW_SCHEMA> extends BaseSliceTrack<
186192
SliceWithRow<T>,
187193
BaseRow & T
188194
> {
189-
protected readonly sqlSource: string;
190195
readonly rootTableName?: string;
191196

192197
constructor(private readonly attrs: DatasetSliceTrackAttrs<T>) {
198+
const dataset = getDataset(attrs);
193199
super(
194200
attrs.trace,
195201
attrs.uri,
196-
{...BASE_ROW, ...attrs.dataset.schema},
202+
{...BASE_ROW, ...dataset.schema},
197203
attrs.sliceLayout,
198204
attrs.initialMaxDepth,
199205
attrs.instantStyle?.width,
200206
);
201-
const {dataset} = attrs;
202-
203-
// This is the minimum viable implementation that the source dataset must
204-
// implement for the track to work properly. Typescript should enforce this
205-
// now, but typescript can be worked around, and checking it is cheap.
206-
// Better to error out early.
207-
assertTrue(this.attrs.dataset.implements(rowSchema));
208-
209-
this.sqlSource = this.generateRenderQuery(dataset);
210207
this.rootTableName = attrs.rootTableName;
211208
}
212209

213210
override rowToSlice(row: BaseRow & T): SliceWithRow<T> {
214211
const slice = this.rowToSliceBase(row);
215212
const title = this.getTitle(row);
216213
const color = this.getColor(row, title);
217-
214+
const dataset = getDataset(this.attrs);
218215
// Take a copy of the row, only copying the keys listed in the schema.
219-
const cols = Object.keys(this.attrs.dataset.schema);
216+
const cols = Object.keys(dataset.schema);
220217
const clonedRow = Object.fromEntries(
221218
Object.entries(row).filter(([key]) => cols.includes(key)),
222219
) as T;
@@ -276,11 +273,15 @@ export class DatasetSliceTrack<T extends ROW_SCHEMA> extends BaseSliceTrack<
276273
}
277274

278275
override getSqlSource(): string {
279-
return this.sqlSource;
276+
const dataset =
277+
typeof this.attrs.dataset === 'function'
278+
? this.attrs.dataset()
279+
: this.attrs.dataset;
280+
return this.generateRenderQuery(dataset);
280281
}
281282

282283
getDataset() {
283-
return this.attrs.dataset;
284+
return getDataset(this.attrs);
284285
}
285286

286287
detailsPanel(sel: TrackEventSelection): TrackEventDetailsPanel | undefined {
@@ -301,7 +302,8 @@ export class DatasetSliceTrack<T extends ROW_SCHEMA> extends BaseSliceTrack<
301302
async getSelectionDetails(
302303
id: number,
303304
): Promise<TrackEventDetails | undefined> {
304-
const {trace, dataset} = this.attrs;
305+
const {trace} = this.attrs;
306+
const dataset = getDataset(this.attrs);
305307
const result = await trace.engine.query(`
306308
SELECT *
307309
FROM (${dataset.query()})

0 commit comments

Comments
 (0)