Skip to content

Commit

Permalink
Merge pull request #7917 from tinyspeck/sarabee-vtadmin-web-schema-vi…
Browse files Browse the repository at this point in the history
…ndexes

[vtadmin-web] Display vindex data on Schema view
  • Loading branch information
ajm188 authored Apr 22, 2021
2 parents 4bb1ee7 + 58ed5c4 commit 5a03c07
Show file tree
Hide file tree
Showing 6 changed files with 274 additions and 3 deletions.
14 changes: 14 additions & 0 deletions web/vtadmin/src/api/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,20 @@ export const fetchTablets = async () =>
},
});

export interface FetchVSchemaParams {
clusterID: string;
keyspace: string;
}

export const fetchVSchema = async ({ clusterID, keyspace }: FetchVSchemaParams) => {
const { result } = await vtfetch(`/api/vschema/${clusterID}/${keyspace}`);

const err = pb.VSchema.verify(result);
if (err) throw Error(err);

return pb.VSchema.create(result);
};

export const fetchWorkflows = async () => {
const { result } = await vtfetch(`/api/workflows`);

Expand Down
22 changes: 22 additions & 0 deletions web/vtadmin/src/components/routes/Schema.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@
}
}

.container {
display: grid;
grid-gap: 16px;
}

.panel {
border: solid 1px var(--colorScaffoldingHighlight);
border-radius: 6px;
Expand Down Expand Up @@ -71,3 +76,20 @@
display: block;
font-size: 5.6rem;
}

.skCol {
// Minimize column width
width: 1%;
}

.skBadge {
border: solid 1px var(--colorPrimary50);
border-radius: 6px;
color: var(--colorPrimary);
display: inline-block;
font-size: var(--fontSizeSmall);
margin: 0 8px;
padding: 4px 8px;
text-transform: uppercase;
white-space: nowrap;
}
71 changes: 68 additions & 3 deletions web/vtadmin/src/components/routes/Schema.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ import * as React from 'react';
import { Link, useParams } from 'react-router-dom';

import style from './Schema.module.scss';
import { useSchema } from '../../hooks/api';
import { useSchema, useVSchema } from '../../hooks/api';
import { Code } from '../Code';
import { useDocumentTitle } from '../../hooks/useDocumentTitle';
import { getVindexesForTable } from '../../util/vschemas';

interface RouteParams {
clusterID: string;
Expand All @@ -29,10 +30,11 @@ interface RouteParams {

export const Schema = () => {
const { clusterID, keyspace, table } = useParams<RouteParams>();
const { data, error, isError, isLoading, isSuccess } = useSchema({ clusterID, keyspace, table });

useDocumentTitle(`${table} (${keyspace})`);

const { data, error, isError, isLoading, isSuccess } = useSchema({ clusterID, keyspace, table });
const vschemaQuery = useVSchema({ clusterID, keyspace });

const tableDefinition = React.useMemo(
() =>
data && Array.isArray(data.table_definitions)
Expand All @@ -41,6 +43,11 @@ export const Schema = () => {
[data, table]
);

const tableVindexes = React.useMemo(
() => (vschemaQuery.data ? getVindexesForTable(vschemaQuery.data, table) : []),
[vschemaQuery.data, table]
);

const is404 = isSuccess && !tableDefinition;

return (
Expand Down Expand Up @@ -98,6 +105,64 @@ export const Schema = () => {
<h3>Table Definition</h3>
<Code code={tableDefinition.schema} />
</section>

{!!tableVindexes.length && (
<section className={style.panel}>
<h3>Vindexes</h3>
<p>
A Vindex provides a way to map a column value to a keyspace ID. Since each shard in
Vitess covers a range of keyspace ID values, this mapping can be used to identify which
shard contains a row.{' '}
<a
href="https://vitess.io/docs/reference/features/vindexes/"
target="_blank"
rel="noopen noreferrer"
>
Learn more about Vindexes in the Vitess documentation.
</a>
</p>

<table>
<thead>
<tr>
<th>Vindex</th>
<th>Columns</th>
<th>Type</th>
<th>Params</th>
<th className={style.skCol} />
</tr>
</thead>
<tbody className="font-family-monospace">
{tableVindexes.map((v, vdx) => {
const columns = v.column ? [v.column] : v.columns;
return (
<tr key={v.name}>
<td>{v.name}</td>
<td>{(columns || []).join(', ')}</td>
<td>{v.meta?.type}</td>
<td>
{v.meta?.params ? (
Object.entries(v.meta.params).map(([k, val]) => (
<div key={k}>
<strong>{k}: </strong> {val}
</div>
))
) : (
<span className="font-size-small text-color-secondary">
N/A
</span>
)}
</td>
<td>
{vdx === 0 && <span className={style.skBadge}>Sharding Key</span>}
</td>
</tr>
);
})}
</tbody>
</table>
</section>
)}
</div>
)}
</div>
Expand Down
9 changes: 9 additions & 0 deletions web/vtadmin/src/hooks/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import {
FetchSchemaParams,
fetchSchemas,
fetchTablets,
fetchVSchema,
FetchVSchemaParams,
fetchWorkflow,
fetchWorkflows,
} from '../api/http';
Expand Down Expand Up @@ -105,6 +107,13 @@ export const useSchema = (params: FetchSchemaParams, options?: UseQueryOptions<p
});
};

/**
* useVSchema is a query hook that fetches a single vschema definition for the given parameters.
*/
export const useVSchema = (params: FetchVSchemaParams, options?: UseQueryOptions<pb.VSchema, Error> | undefined) => {
return useQuery(['vschema', params], () => fetchVSchema(params));
};

/**
* useWorkflow is a query hook that fetches a single workflow for the given parameters.
*/
Expand Down
132 changes: 132 additions & 0 deletions web/vtadmin/src/util/vschemas.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
/**
* Copyright 2021 The Vitess Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { vtadmin as pb } from '../proto/vtadmin';
import * as vs from './vschemas';

describe('getVindexesForTable', () => {
const tests: {
name: string;
input: Parameters<typeof vs.getVindexesForTable>;
expected: ReturnType<typeof vs.getVindexesForTable>;
}[] = [
{
name: 'should return column vindexes',
input: [
pb.VSchema.create({
v_schema: {
tables: {
customer: {
column_vindexes: [{ column: 'customer_id', name: 'hash' }],
},
},
vindexes: {
hash: { type: 'hash' },
},
},
}),
'customer',
],
expected: [
{
column: 'customer_id',
name: 'hash',
meta: { type: 'hash' },
},
],
},
{
name: 'should return column vindexes + metadata',
input: [
pb.VSchema.create({
v_schema: {
tables: {
dogs: {
column_vindexes: [
{ column: 'id', name: 'hash' },
{ name: 'dogs_domain_vdx', columns: ['domain', 'is_good_dog'] },
],
},
},
vindexes: {
hash: { type: 'hash' },
dogs_domain_vdx: {
type: 'lookup_hash',
owner: 'dogs',
params: {
from: 'domain,is_good_dog',
table: 'dogs_domain_idx',
to: 'id',
},
},
},
},
}),
'dogs',
],
expected: [
{
column: 'id',
name: 'hash',
meta: { type: 'hash' },
},
{
columns: ['domain', 'is_good_dog'],
name: 'dogs_domain_vdx',
meta: {
owner: 'dogs',
params: {
from: 'domain,is_good_dog',
table: 'dogs_domain_idx',
to: 'id',
},
type: 'lookup_hash',
},
},
],
},
{
name: 'should handle vschemas where the given table is not defined',
input: [
pb.VSchema.create({
v_schema: {
tables: {
customer: {
column_vindexes: [{ column: 'customer_id', name: 'hash' }],
},
},
vindexes: {
hash: { type: 'hash' },
},
},
}),
'does-not-exist',
],
expected: [],
},
];

test.each(tests.map(Object.values))(
'%s',
(
name: string,
input: Parameters<typeof vs.getVindexesForTable>,
expected: ReturnType<typeof vs.getVindexesForTable>
) => {
const result = vs.getVindexesForTable(...input);
expect(result).toEqual(expected);
}
);
});
29 changes: 29 additions & 0 deletions web/vtadmin/src/util/vschemas.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/**
* Copyright 2021 The Vitess Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { vtadmin as pb } from '../proto/vtadmin';

/**
* getVindexesForTable returns Vindexes + Vindex metadata for the given table.
*/
export const getVindexesForTable = (vschema: pb.VSchema, tableName: string) => {
const table = (vschema.v_schema?.tables || {})[tableName];
if (!table) return [];

return (table.column_vindexes || []).map((cv) => ({
...cv,
meta: cv.name ? (vschema.v_schema?.vindexes || {})[cv.name] : null,
}));
};

0 comments on commit 5a03c07

Please sign in to comment.