Skip to content

Commit 3bcf06d

Browse files
Rachel ChenRachel Chen
authored andcommitted
feat(cbrs): UI
1 parent beabc91 commit 3bcf06d

File tree

12 files changed

+290
-47
lines changed

12 files changed

+290
-47
lines changed

snuba/admin/rpc/rpc_queries.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
from snuba import settings
44
from snuba.web.rpc.common.exceptions import BadSnubaRPCRequestException
5+
from snuba.web.rpc.storage_routing.routing_strategies.storage_routing import (
6+
BaseRoutingStrategy,
7+
)
58

69

710
def _validate_projects_in_query(project_ids: List[int]) -> None:
@@ -30,3 +33,7 @@ def validate_request_meta(request_proto: Any) -> None:
3033
project_ids = list(meta.project_ids)
3134
_validate_org_ids_in_query(org_id)
3235
_validate_projects_in_query(project_ids)
36+
37+
38+
def get_routing_strategies() -> List[str]:
39+
return list(BaseRoutingStrategy.all_names())

snuba/admin/static/api_client.tsx

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ import {
3535
CardinalityQueryResult,
3636
} from "SnubaAdmin/cardinality_analyzer/types";
3737

38-
import { AllocationPolicy } from "SnubaAdmin/capacity_management/types";
38+
import { AllocationPolicy, Entity } from "SnubaAdmin/capacity_management/types";
3939

4040
import { ReplayInstruction, Topic } from "SnubaAdmin/dead_letter_queue/types";
4141
import { AutoReplacementsBypassProjectsData } from "SnubaAdmin/auto_replacements_bypass_projects/types";
@@ -83,17 +83,18 @@ interface Client {
8383
getAllMigrationGroups: () => Promise<MigrationGroupResult[]>;
8484
runMigration: (req: RunMigrationRequest) => Promise<RunMigrationResult>;
8585
getAllowedTools: () => Promise<AllowedTools>;
86+
getRoutingStrategies: () => Promise<string[]>;
8687
getStoragesWithAllocationPolicies: () => Promise<string[]>;
87-
getAllocationPolicies: (storage: string) => Promise<AllocationPolicy[]>;
88+
getAllocationPolicies: (entity: Entity) => Promise<AllocationPolicy[]>;
8889
setAllocationPolicyConfig: (
89-
storage: string,
90+
entity: Entity,
9091
policy: string,
9192
key: string,
9293
value: string,
9394
params: object,
9495
) => Promise<void>;
9596
deleteAllocationPolicyConfig: (
96-
storage: string,
97+
entity_name: string,
9798
policy: string,
9899
key: string,
99100
params: object,
@@ -445,31 +446,39 @@ function Client(): Client {
445446
}).then((resp) => resp.json());
446447
},
447448

449+
getRoutingStrategies: () => {
450+
const url = baseUrl + "routing_strategies";
451+
return fetch(url, {
452+
headers: { "Content-Type": "application/json" },
453+
}).then((resp) => resp.json());
454+
},
455+
448456
getStoragesWithAllocationPolicies: () => {
449457
const url = baseUrl + "storages_with_allocation_policies";
450458
return fetch(url, {
451459
headers: { "Content-Type": "application/json" },
452460
}).then((resp) => resp.json());
453461
},
454-
getAllocationPolicies: (storage: string) => {
462+
getAllocationPolicies: (entity: Entity) => {
455463
const url =
456-
baseUrl + "allocation_policy_configs/" + encodeURIComponent(storage);
464+
baseUrl + "allocation_policy_configs/" + entity.type + "/" + encodeURIComponent(entity.name);
457465
return fetch(url, {
458466
headers: { "Content-Type": "application/json" },
459467
}).then((resp) => resp.json());
460468
},
461469
setAllocationPolicyConfig: (
462-
storage: string,
470+
entity: Entity,
463471
policy: string,
464472
key: string,
465473
value: string,
466474
params: object,
467475
) => {
476+
const body = entity.type === "storage" ? JSON.stringify({ storage: entity.name, policy, key, value, params }) : JSON.stringify({ strategy: entity.name, policy, key, value, params });
468477
const url = baseUrl + "allocation_policy_config";
469478
return fetch(url, {
470479
headers: { "Content-Type": "application/json" },
471480
method: "POST",
472-
body: JSON.stringify({ storage, policy, key, value, params }),
481+
body: body,
473482
}).then((res) => {
474483
if (res.ok) {
475484
return;
@@ -482,7 +491,7 @@ function Client(): Client {
482491
});
483492
},
484493
deleteAllocationPolicyConfig: (
485-
storage: string,
494+
entity_name: string,
486495
policy: string,
487496
key: string,
488497
params: object,
@@ -491,7 +500,7 @@ function Client(): Client {
491500
return fetch(url, {
492501
headers: { "Content-Type": "application/json" },
493502
method: "DELETE",
494-
body: JSON.stringify({ storage, policy, key, params }),
503+
body: JSON.stringify({ entity_name, policy, key, params }),
495504
}).then((res) => {
496505
if (res.ok) {
497506
return;

snuba/admin/static/capacity_management/allocation_policy.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import React, { useEffect, useState } from "react";
33
import { Table, createCustomTableStyles } from "../table";
44
import { COLORS } from "SnubaAdmin/theme";
55
import Client from "SnubaAdmin/api_client";
6-
import { AllocationPolicy, AllocationPolicyConfig } from "SnubaAdmin/capacity_management/types";
6+
import { AllocationPolicy, AllocationPolicyConfig, Entity } from "SnubaAdmin/capacity_management/types";
77
import { containerStyle, linkStyle, paragraphStyle } from "SnubaAdmin/capacity_management/styles";
88
import { getReadonlyRow } from "SnubaAdmin/capacity_management/row_data";
99
import EditConfigModal from "SnubaAdmin/capacity_management/edit_config_modal";
@@ -39,10 +39,10 @@ function getTableColor(configs: AllocationPolicyConfig[]): string {
3939

4040
function AllocationPolicyConfigs(props: {
4141
api: Client;
42-
storage: string;
42+
entity: Entity;
4343
policy: AllocationPolicy;
4444
}) {
45-
const { api, storage, policy } = props;
45+
const { api, entity, policy } = props;
4646

4747
const [configs, setConfigs] = useState<AllocationPolicyConfig[]>([]);
4848

@@ -69,7 +69,7 @@ function AllocationPolicyConfigs(props: {
6969
function deleteConfig(toDelete: AllocationPolicyConfig) {
7070
api
7171
.deleteAllocationPolicyConfig(
72-
storage,
72+
entity.name,
7373
policy.policy_name,
7474
toDelete.name,
7575
toDelete.params
@@ -89,7 +89,7 @@ function AllocationPolicyConfigs(props: {
8989
function saveConfig(config: AllocationPolicyConfig) {
9090
api
9191
.setAllocationPolicyConfig(
92-
storage,
92+
entity,
9393
policy.policy_name,
9494
config.name,
9595
config.value,

snuba/admin/static/capacity_management/index.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ function CapacityManagement(props: { api: Client }) {
3131

3232
function loadAllocationPolicies(storage: string) {
3333
api
34-
.getAllocationPolicies(storage)
34+
.getAllocationPolicies({ type: "storage", name: storage })
3535
.then((res) => {
3636
setAllocationPolicies(res);
3737
})
@@ -55,7 +55,7 @@ function CapacityManagement(props: { api: Client }) {
5555
{policies.map((policy: AllocationPolicy) => (
5656
<AllocationPolicyConfigs
5757
api={api}
58-
storage={selectedStorage}
58+
entity={{ type: "storage", name: selectedStorage }}
5959
policy={policy}
6060
key={selectedStorage + policy.policy_name}
6161
/>

snuba/admin/static/capacity_management/types.tsx

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,41 @@ type RowData = {
3737
edit: ReactNode;
3838
};
3939

40+
interface StorageEntity {
41+
type: "storage";
42+
name: string;
43+
}
44+
45+
interface StrategyEntity {
46+
type: "strategy";
47+
name: string;
48+
}
49+
50+
type Entity = StorageEntity | StrategyEntity;
51+
52+
// Usage
53+
function getEntityName(entity: Entity): string {
54+
return entity.name; // Works for both types
55+
}
56+
57+
function isStorage(entity: Entity): entity is StorageEntity {
58+
return entity.type === "storage";
59+
}
60+
61+
function isStrategy(entity: Entity): entity is StrategyEntity {
62+
return entity.type === "strategy";
63+
}
64+
4065
export {
4166
AllocationPolicy,
4267
AllocationPolicyConfig,
4368
AllocationPolicyOptionalConfigDefinition,
4469
AllocationPolicyConfigParams,
4570
RowData,
71+
Entity,
72+
getEntityName,
73+
isStorage,
74+
isStrategy,
75+
StorageEntity,
76+
StrategyEntity,
4677
};

snuba/admin/static/cbrs/index.tsx

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import React, { useEffect, useState } from "react";
2+
import Client from "SnubaAdmin/api_client";
3+
import { AllocationPolicyConfigs } from "SnubaAdmin/capacity_management/allocation_policy";
4+
import { AllocationPolicy } from "SnubaAdmin/capacity_management/types";
5+
import { CustomSelect, getParamFromStorage } from "SnubaAdmin/select";
6+
import { COLORS } from "SnubaAdmin/theme";
7+
8+
function CapacityBasedRoutingSystem(props: { api: Client }) {
9+
const { api } = props;
10+
11+
const [strategies, setStrategies] = useState<string[]>([]);
12+
const [selectedStrategy, setStrategy] = useState<string | undefined>();
13+
const [allocationPolicies, setAllocationPolicies] = useState<
14+
AllocationPolicy[]
15+
>([]);
16+
17+
useEffect(() => {
18+
api.getRoutingStrategies().then((res) => {
19+
setStrategies(res);
20+
const previousStrategy = getParamFromStorage("strategy");
21+
if (previousStrategy) {
22+
selectStrategy(previousStrategy);
23+
}
24+
});
25+
}, []);
26+
27+
function selectStrategy(strategy: string) {
28+
setStrategy(strategy);
29+
loadAllocationPolicies(strategy);
30+
}
31+
32+
function loadAllocationPolicies(strategy: string) {
33+
api
34+
.getAllocationPolicies({ type: "strategy", name: strategy })
35+
.then((res) => {
36+
setAllocationPolicies(res);
37+
})
38+
.catch((err) => {
39+
window.alert(err);
40+
});
41+
}
42+
43+
function renderPolicies(policies: AllocationPolicy[]) {
44+
if (!selectedStrategy) {
45+
return <p>Strategy not selected.</p>;
46+
}
47+
if (policies.length == 0) {
48+
return null;
49+
}
50+
return (
51+
<div>
52+
<p style={policyTypeStyle}>
53+
Policy Type: {policies[0].query_type.toUpperCase()}
54+
</p>
55+
{policies.map((policy: AllocationPolicy) => (
56+
<AllocationPolicyConfigs
57+
api={api}
58+
entity={{ type: "strategy", name: selectedStrategy }}
59+
policy={policy}
60+
key={selectedStrategy + policy.policy_name}
61+
/>
62+
))}
63+
</div>
64+
);
65+
}
66+
67+
return (
68+
<div>
69+
<p>
70+
Strategy:
71+
<CustomSelect
72+
value={selectedStrategy || ""}
73+
onChange={selectStrategy}
74+
name="strategy"
75+
options={strategies}
76+
/>
77+
</p>
78+
79+
{renderPolicies(
80+
allocationPolicies.filter((policy) => policy.query_type == "select")
81+
)}
82+
{renderPolicies(
83+
allocationPolicies.filter((policy) => policy.query_type == "delete")
84+
)}
85+
</div>
86+
);
87+
}
88+
89+
const policyTypeStyle = {
90+
fontSize: 18,
91+
fontWeight: 600,
92+
color: COLORS.HEADER_TEXT,
93+
backgroundColor: COLORS.TEXT_LIGHTER,
94+
maxWidth: "100%",
95+
margin: "10px 0px",
96+
padding: "5px",
97+
};
98+
99+
export default CapacityBasedRoutingSystem;

snuba/admin/static/data.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import DeleteTool from "SnubaAdmin/delete_tool";
1818
import ViewCustomJobs from "SnubaAdmin/manual_jobs";
1919
import DatabaseClusters from "./database_clusters";
2020
import RpcEndpoints from "SnubaAdmin/rpc_endpoints";
21+
import CapacityBasedRoutingSystem from "SnubaAdmin/cbrs";
2122

2223
const NAV_ITEMS = [
2324
{ id: "overview", display: "🤿 Snuba Admin", component: Welcome },
@@ -112,6 +113,11 @@ const NAV_ITEMS = [
112113
display: "🗂️ Database Clusters",
113114
component: DatabaseClusters,
114115
},
116+
{
117+
id: "capacity-based-routing-system",
118+
display: "🔄 Capacity Based Routing System",
119+
component: CapacityBasedRoutingSystem,
120+
},
115121
];
116122

117123
export { NAV_ITEMS };

snuba/admin/static/tests/capacity_management/allocation_policies.spec.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ it("should populate configs table upon render", async () => {
4343
let { getByText, getByTestId } = render(
4444
<AllocationPolicyConfigs
4545
api={Client()}
46-
storage="storage1"
46+
entity={{ type: "storage", name: "storage1" }}
4747
policy={allocationPolicy}
4848
/>
4949
);

snuba/admin/tool_policies.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ class AdminTools(Enum):
2828
AUDIT_LOG = "audit-log"
2929
KAFKA = "kafka"
3030
CAPACITY_MANAGEMENT = "capacity-management"
31+
CAPACITY_BASED_ROUTING_SYSTEM = "capacity-based-routing-system"
3132
PRODUCTION_QUERIES = "production-queries"
3233
CARDINALITY_ANALYZER = "cardinality-analyzer"
3334
SNUBA_EXPLAIN = "snuba-explain"

0 commit comments

Comments
 (0)