diff --git a/src/brad/planner/abstract.py b/src/brad/planner/abstract.py
index a82af7a1..f2edf09c 100644
--- a/src/brad/planner/abstract.py
+++ b/src/brad/planner/abstract.py
@@ -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
diff --git a/src/brad/ui/manager_impl.py b/src/brad/ui/manager_impl.py
index 2a65170e..93eef308 100644
--- a/src/brad/ui/manager_impl.py
+++ b/src/brad/ui/manager_impl.py
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
diff --git a/src/brad/ui/models.py b/src/brad/ui/models.py
index 9764beb0..aeb7b319 100644
--- a/src/brad/ui/models.py
+++ b/src/brad/ui/models.py
@@ -1,3 +1,4 @@
+import enum
from typing import List, Dict, Optional
from pydantic import BaseModel, AwareDatetime
@@ -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]
diff --git a/ui/src/App.jsx b/ui/src/App.jsx
index 6d299ed3..4e4c2060 100644
--- a/ui/src/App.jsx
+++ b/ui/src/App.jsx
@@ -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,
@@ -115,7 +117,7 @@ function App() {
return (
<>
-