Skip to content
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

Render current system status and transitioning blueprint #484

Merged
merged 3 commits into from
Mar 30, 2024
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
3 changes: 3 additions & 0 deletions src/brad/planner/abstract.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,3 +179,6 @@ async def _notify_new_blueprint(
for callback in self._callbacks:
tasks.append(asyncio.create_task(callback(blueprint, score, trigger)))
await asyncio.gather(*tasks)

def replan_in_progress(self) -> bool:
return self._replan_in_progress
26 changes: 25 additions & 1 deletion src/brad/ui/manager_impl.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from brad.blueprint import Blueprint
from brad.blueprint.table import Table
from brad.blueprint.manager import BlueprintManager
from brad.planner.abstract import BlueprintPlanner
from brad.config.engine import Engine
from brad.config.file import ConfigFile
from brad.daemon.monitor import Monitor
Expand All @@ -24,6 +25,7 @@
DisplayableVirtualEngine,
VirtualInfrastructure,
DisplayableTable,
Status,
)
from brad.daemon.front_end_metrics import FrontEndMetric
from brad.daemon.system_event_logger import SystemEventLogger, SystemEventRecord
Expand All @@ -43,6 +45,7 @@ def __init__(
self.monitor = monitor
self.blueprint_mgr = blueprint_mgr
self.system_event_logger = system_event_logger
self.planner: Optional[BlueprintPlanner] = None

async def serve_forever(self) -> None:
global manager # pylint: disable=global-statement
Expand Down Expand Up @@ -169,7 +172,20 @@ def get_system_state(filter_tables_for_demo: bool = False) -> SystemState:
if t.name in txn_tables:
t.is_writer = True
virtual_infra = VirtualInfrastructure(engines=[vdbe1, vdbe2])
system_state = SystemState(virtual_infra=virtual_infra, blueprint=dbp)

status = _determine_current_status(manager)
if status is Status.Transitioning:
next_blueprint = manager.blueprint_mgr.get_transition_metadata().next_blueprint
assert next_blueprint is not None
next_dbp = DisplayableBlueprint.from_blueprint(next_blueprint)
else:
next_dbp = None
system_state = SystemState(
status=status,
virtual_infra=virtual_infra,
blueprint=dbp,
next_blueprint=next_dbp,
)
_add_reverse_mapping_temp(system_state)
return system_state

Expand Down Expand Up @@ -222,6 +238,14 @@ def _add_reverse_mapping_temp(system_state: SystemState) -> None:
table.mapped_to.append(veng_name)


def _determine_current_status(manager_impl: UiManagerImpl) -> Status:
if manager_impl.planner is not None and manager_impl.planner.replan_in_progress():
return Status.Planning
if manager_impl.blueprint_mgr.get_transition_metadata().next_blueprint is not None:
return Status.Transitioning
return Status.Running


@app.get("/api/1/system_events")
def get_system_events() -> List[SystemEventRecord]:
assert manager is not None
Expand Down
9 changes: 9 additions & 0 deletions src/brad/ui/models.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import enum
from typing import List, Dict, Optional
from pydantic import BaseModel, AwareDatetime

Expand Down Expand Up @@ -95,6 +96,14 @@ class VirtualInfrastructure(BaseModel):
engines: List[DisplayableVirtualEngine]


class Status(enum.Enum):
Running = "running"
Planning = "planning"
Transitioning = "transitioning"


class SystemState(BaseModel):
status: Status
virtual_infra: VirtualInfrastructure
blueprint: DisplayableBlueprint
next_blueprint: Optional[DisplayableBlueprint]
5 changes: 4 additions & 1 deletion ui/src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ const REFRESH_INTERVAL_MS = 30 * 1000;

function App() {
const [systemState, setSystemState] = useState({
status: "running",
blueprint: null,
virtual_infra: null,
next_blueprint: null,
});
const [highlight, setHighlight] = useState({
hoverEngine: null,
Expand Down Expand Up @@ -115,7 +117,7 @@ function App() {

return (
<>
<Header />
<Header status={systemState.status} />
<div class="body-container">
<div class="column" style={{ flexGrow: 3 }}>
<h2 class="col-h2">Data Infrastructure</h2>
Expand All @@ -129,6 +131,7 @@ function App() {
/>
<BlueprintView
blueprint={systemState.blueprint}
nextBlueprint={systemState.next_blueprint}
highlight={highlight}
onTableHoverEnter={onTableHoverEnter}
onTableHoverExit={onTableHoverExit}
Expand Down
12 changes: 12 additions & 0 deletions ui/src/components/BlueprintView.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,19 @@ import Panel from "./Panel";
import PhysDbView from "./PhysDbView";
import "./styles/BlueprintView.css";

function findNextEngine(engineName, nextBlueprint) {
if (nextBlueprint == null) return null;
for (const engine of nextBlueprint.engines) {
if (engine.name === engineName) {
return engine;
}
}
return null;
}

function BlueprintView({
blueprint,
nextBlueprint,
highlight,
onTableHoverEnter,
onTableHoverExit,
Expand All @@ -21,6 +32,7 @@ function BlueprintView({
highlight={highlight}
onTableHoverEnter={onTableHoverEnter}
onTableHoverExit={onTableHoverExit}
nextEngine={findNextEngine(name, nextBlueprint)}
/>
))}
</div>
Expand Down
20 changes: 15 additions & 5 deletions ui/src/components/Header.jsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,38 @@
import "./styles/Header.css";
import bradLogo from "../assets/brad_logo.png";

function statusToDisplay(status) {
if (status === "transitioning") {
return "Transitioning...";
} else if (status === "planning") {
return "Running planner...";
} else {
return "Running";
}
}

function StatusText({ status, schema }) {
if (!!schema) {
return (
<div class="header-status-text">
{status} ({schema})
{statusToDisplay(status)} ({schema})
</div>
);
} else {
return <div class="header-status-text">{status}</div>;
return <div class="header-status-text">{statusToDisplay(status)}</div>;
}
}

function StatusIndicator({ status, schema }) {
return (
<div class="header-status">
<div class="header-status-icon"></div>
<div class={`header-status-icon ${status}`}></div>
<StatusText status={status} schema={schema} />
</div>
);
}

function Header() {
function Header({ status }) {
return (
<div class="header">
<div class="header-inner">
Expand All @@ -34,7 +44,7 @@ function Header() {
<strong>BRAD</strong> Dashboard
</div>
</div>
<StatusIndicator status="Running" schema="imdb_extended_100g" />
<StatusIndicator status={status} schema="imdb_extended_100g" />
</div>
</div>
);
Expand Down
33 changes: 33 additions & 0 deletions ui/src/components/PhysDbView.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,45 @@ import {
sortTablesToHoist,
} from "../highlight";

function addedTables(tables, nextEngine) {
if (nextEngine == null) return [];
const added = [];
const currTableSet = new Set();
for (const currTable of tables) {
currTableSet.add(currTable.name);
}
for (const table of nextEngine.tables) {
if (currTableSet.has(table.name)) continue;
added.push(table);
}
return added;
}

function PhysDbView({
name,
provisioning,
tables,
highlight,
onTableHoverEnter,
onTableHoverExit,
nextEngine,
}) {
const physDbName = name;
const sortedTables = sortTablesToHoist(highlight, physDbName, false, tables);
const addedTablesList = addedTables(tables, nextEngine);

return (
<div
class={`physdb-view ${highlightEngineViewClass(highlight, physDbName, false)}`}
>
<DbCylinder color="blue">{name}</DbCylinder>
<div class="physdb-view-prov">{provisioning}</div>
{nextEngine && (
<div class="physdb-view-prov transition">
{nextEngine.provisioning ? "→ " : ""}
{nextEngine.provisioning}
</div>
)}
<div class="db-table-set">
{sortedTables.map(({ name, is_writer, mapped_to }) => (
<TableView
Expand All @@ -43,6 +65,17 @@ function PhysDbView({
onTableHoverExit={onTableHoverExit}
/>
))}
{addedTablesList.map(({ name, is_writer }) => (
<TableView
key={name}
name={name}
isWriter={is_writer}
color="blue"
highlightClass="dim"
onTableHoverEnter={() => {}}
onTableHoverExit={() => {}}
/>
))}
</div>
</div>
);
Expand Down
13 changes: 13 additions & 0 deletions ui/src/components/styles/Header.css
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,19 @@
height: 12px;
border-radius: 50%;
margin: -2px 15px 0 0;
}

.header-status-icon.running {
background-color: #51a965;
box-shadow: 0 0 5px #51a965;
}

.header-status-icon.transitioning {
background-color: #ec8405;
box-shadow: 0 0 5px #ec8405;
}

.header-status-icon.planning {
background-color: #517aa9;
box-shadow: 0 0 5px #517aa9;
}
4 changes: 4 additions & 0 deletions ui/src/components/styles/PhysDbView.css
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@
height: 30px;
}

.physdb-view-prov.transition {
color: #888;
}

.physdb-view.dim .db-cylinder,
.physdb-view.dim .physdb-view-prov {
opacity: 0.3;
Expand Down