Skip to content

Commit

Permalink
Block Viewer: overlapping blocks issue resolved
Browse files Browse the repository at this point in the history
Signed-off-by: Namanl2001 <namanlakhwani@gmail.com>
  • Loading branch information
Namanl2001 committed Feb 18, 2021
1 parent e4aa01a commit 703bd0a
Show file tree
Hide file tree
Showing 6 changed files with 353 additions and 49 deletions.
68 changes: 34 additions & 34 deletions pkg/ui/bindata.go

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion pkg/ui/react-app/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ const App: FC<PathPrefixProps & ThanosComponentProps> = ({ pathPrefix, thanosCom
<Blocks path="/loaded" pathPrefix={pathPrefix} view="loaded" />
<NotFound pathPrefix={pathPrefix} default defaultRoute={defaultRouteConfig[thanosComponent]} />
</Router>
.
</QueryParamProvider>
</Container>
</ErrorBoundary>
Expand Down
18 changes: 11 additions & 7 deletions pkg/ui/react-app/src/thanos/pages/blocks/SourceView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,17 @@ export const SourceView: FC<SourceViewProps> = ({ data, title, gridMaxTime, grid
</div>
<div className={styles.rowsContainer}>
{Object.keys(data).map(k => (
<BlocksRow
selectBlock={selectBlock}
blocks={data[k]}
key={k}
gridMaxTime={gridMaxTime}
gridMinTime={gridMinTime}
/>
<React.Fragment key={k}>
{data[k].map((b, i) => (
<BlocksRow
selectBlock={selectBlock}
blocks={b}
key={`${k}-${i}`}
gridMaxTime={gridMaxTime}
gridMinTime={gridMinTime}
/>
))}
</React.Fragment>
))}
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion pkg/ui/react-app/src/thanos/pages/blocks/block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,5 @@ export interface LabelSet {
}

export interface BlocksPool {
[key: string]: Block[];
[key: string]: Block[][];
}
258 changes: 258 additions & 0 deletions pkg/ui/react-app/src/thanos/pages/blocks/helpers.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,258 @@
import { sortBlocks, isOverlapping } from './helpers';

// number of blocks in data: 8.
const data = {
blocks: [
{
compaction: {
level: 1,
sources: ['01EWZCKPP4K0WYRTZC9RPRM5QK'],
},
minTime: 1608034200000,
maxTime: 1608034500000,
stats: {
numSamples: 6634538,
numSeries: 2334,
numChunks: 51057,
},
thanos: {
downsample: {
resolution: 0,
},
labels: {
monitor: 'prometheus_one',
},
source: 'sidecar',
},
ulid: '01EWZCKPP4K0WYRTZC9RPRM5QK',
version: 1,
},

{
compaction: {
level: 1,
sources: ['01ESK5B1WQB6QEZQ4P0YCQXEC4'],
},
minTime: 1608034200000,
maxTime: 1608034500000,
stats: {
numSamples: 6634538,
numSeries: 2334,
numChunks: 51057,
},
thanos: {
downsample: {
resolution: 0,
},
labels: {
monitor: 'prometheus_one',
},
source: 'sidecar',
},
ulid: '01ESK5B1WQB6QEZQ4P0YCQXEC4',
version: 1,
},
{
compaction: {
level: 1,
sources: ['01ET8F8C73GGXH279R6YMTWFHY'],
},
minTime: 1608034500000,
maxTime: 1608034800000,
stats: {
numSamples: 6979750,
numSeries: 2333,
numChunks: 58325,
},
thanos: {
downsample: {
resolution: 0,
},
labels: {
monitor: 'prometheus_one',
},
source: 'sidecar',
},
ulid: '01ET8F8C73GGXH279R6YMTWFHY',
version: 1,
},
{
compaction: {
level: 1,
sources: ['01EWZCA2CFC5CPJE8CF9TXBW9H'],
},
minTime: 1608034500000,
maxTime: 1608034800000,
stats: {
numSamples: 6979750,
numSeries: 2333,
numChunks: 58325,
},
thanos: {
downsample: {
resolution: 0,
},
labels: {
monitor: 'prometheus_one',
},
source: 'sidecar',
},
ulid: '01EWZCA2CFC5CPJE8CF9TXBW9H',
version: 1,
},
{
compaction: {
level: 1,
sources: ['01EXYEAS52VZW5G1FPV4NPH2D1'],
},
minTime: 1608034500000,
maxTime: 1608034800000,
stats: {
numSamples: 6979750,
numSeries: 2333,
numChunks: 58325,
},
thanos: {
downsample: {
resolution: 0,
},
labels: {
monitor: 'prometheus_one',
},
source: 'sidecar',
},
ulid: '01EXYEAS52VZW5G1FPV4NPH2D1',
version: 1,
},
{
compaction: {
level: 1,
sources: ['01EWZCC9E998R19K8FKSTWP776'],
},
minTime: 1608034400000,
maxTime: 1608034700000,
stats: {
numSamples: 6979750,
numSeries: 2333,
numChunks: 58325,
},
thanos: {
downsample: {
resolution: 0,
},
labels: {
monitor: 'prometheus_one',
},
source: 'sidecar',
},
ulid: '01EWZCC9E998R19K8FKSTWP776',
version: 1,
},
{
compaction: {
level: 1,
sources: ['01EXYE0YB9JYCT48B6673H4YNS'],
},
minTime: 1608034600000,
maxTime: 1608034800000,
stats: {
numSamples: 6979750,
numSeries: 2333,
numChunks: 58325,
},
thanos: {
downsample: {
resolution: 0,
},
labels: {
monitor: 'prometheus_one',
},
source: 'sidecar',
},
ulid: '01EXYE0YB9JYCT48B6673H4YNS',
version: 1,
},
{
compaction: {
level: 1,
sources: ['01EEF8AGCHTPJ1MZ8KH0SEJZ4E'],
},
minTime: 1608034250000,
maxTime: 1608034350000,
stats: {
numSamples: 6979750,
numSeries: 2333,
numChunks: 58325,
},
thanos: {
downsample: {
resolution: 0,
},
labels: {
monitor: 'prometheus_one',
},
source: 'sidecar',
},
ulid: '01EEF8AGCHTPJ1MZ8KH0SEJZ4E',
version: 1,
},
],
label: 'monitor',
};

const sorted = sortBlocks(data.blocks, data.label);
const source = 'prometheus_one';

describe('overlapping blocks', () => {
it('has 1 source', () => {
expect(Object.keys(sorted)).toHaveLength(1);
});

it('has 1 level-resolution', () => {
expect(Object.keys(sorted[source])).toHaveLength(1);
});

const rows = Object.values(sorted[source])[0];
it('has 5 rows', () => {
expect(rows).toHaveLength(5);
});

it('renders 2 blocks in first row', () => {
expect(rows[0]).toHaveLength(2);
});

it('renders 2 blocks in second row', () => {
expect(rows[1]).toHaveLength(2);
});

it('renders 2 blocks in third row', () => {
expect(rows[2]).toHaveLength(2);
});

it('renders 1 block in fourth row', () => {
expect(rows[3]).toHaveLength(1);
});

it('renders 1 block in fifth row', () => {
expect(rows[4]).toHaveLength(1);
});
});

describe('isOverlapping helper', () => {
const b = data.blocks[0];
it('should return true for perfectly overlapping blocks', () => {
expect(isOverlapping({ ...b, minTime: 10, maxTime: 20 }, { ...b, minTime: 10, maxTime: 20 })).toBe(true);
});

it('should return true for partially overlapping blocks', () => {
expect(isOverlapping({ ...b, minTime: 10, maxTime: 20 }, { ...b, minTime: 15, maxTime: 25 })).toBe(true);
});

it('should return false for non-overlapping blocks', () => {
expect(isOverlapping({ ...b, minTime: 10, maxTime: 20 }, { ...b, minTime: 30, maxTime: 40 })).toBe(false);
});

it('should return false if second block starts where first ends (a.maxTime == b.minTime)', () => {
expect(isOverlapping({ ...b, minTime: 10, maxTime: 20 }, { ...b, minTime: 20, maxTime: 30 })).toBe(false);
});
});
55 changes: 49 additions & 6 deletions pkg/ui/react-app/src/thanos/pages/blocks/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,67 @@ const stringify = (map: LabelSet): string => {
return t;
};

export const isOverlapping = (a: Block, b: Block): boolean => {
if (a.minTime <= b.minTime) return b.minTime < a.maxTime;
else return a.minTime < b.maxTime;
};

const determineRow = (block: Block, rows: Block[][], startWithRow: number): number => {
if (rows.length === 0) return 0;

const len = rows[startWithRow]?.length || 0;
if (len === 0) return startWithRow;

if (isOverlapping(rows[startWithRow][len - 1], block)) {
// Blocks are overlapping, try next row.
return determineRow(block, rows, startWithRow + 1);
}
return startWithRow;
};

const splitOverlappingBlocks = (blocks: Block[]): Block[][] => {
const rows: Block[][] = [[]];
if (blocks.length === 0) return rows;

blocks.forEach(b => {
const r = determineRow(b, rows, 0);
if (!rows[r]) rows[r] = [];
rows[r].push(b);
});
return rows;
};

const sortBlocksInRows = (blocks: Block[]): BlocksPool => {
const pool: BlocksPool = {};
const poolWithOverlaps: { [key: string]: Block[] } = {};

blocks
.sort((a, b) => a.thanos.downsample.resolution - b.thanos.downsample.resolution)
.sort((a, b) => {
if (a.compaction.level - b.compaction.level) {
return a.compaction.level - b.compaction.level;
}
if (a.thanos.downsample.resolution - b.thanos.downsample.resolution) {
return a.thanos.downsample.resolution - b.thanos.downsample.resolution;
}
return a.minTime - b.minTime;
})
.forEach(b => {
if (!pool[`${b.compaction.level}-${b.thanos.downsample.resolution}`])
pool[`${b.compaction.level}-${b.thanos.downsample.resolution}`] = [];
const key = `${b.compaction.level}-${b.thanos.downsample.resolution}`;
if (!poolWithOverlaps[key]) poolWithOverlaps[key] = [];

pool[`${b.compaction.level}-${b.thanos.downsample.resolution}`].push(b);
poolWithOverlaps[key].push(b);
});

const pool: BlocksPool = {};
Object.entries(poolWithOverlaps).forEach(([key, blks]) => {
pool[key] = splitOverlappingBlocks(blks);
});

return pool;
};

export const sortBlocks = (blocks: Block[], label: string): { [source: string]: BlocksPool } => {
const titles: { [key: string]: string } = {};
const pool: BlocksPool = {};
const pool: { [key: string]: Block[] } = {};

blocks
.sort((a, b) => a.compaction.level - b.compaction.level)
Expand Down

0 comments on commit 703bd0a

Please sign in to comment.