Skip to content

Commit

Permalink
add replication delay if it's set to replication panel
Browse files Browse the repository at this point in the history
  • Loading branch information
charles-001 committed Feb 10, 2024
1 parent 2433ead commit 8e49f16
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 80 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ options:
--panels What panels to display on startup separated by a comma. Supports: dashboard/processlist/graphs/replication/locks/ddl [default: dashboard,processlist]
--graph-marker What marker to use for graphs (available options: https://tinyurl.com/dolphie-markers) [default: braille]
--pypi-repository What PyPi repository to use when checking for a new version. If not specified, it will use Dolphie's PyPi repository
--hostgroup This is used for creating tabs and connecting to them for hosts you specify in Dolphie's config file under a hostgroup section. As an example, you'll have a section called [cluster1] then below it will be listed each host on a new line in the format key=host where key can be anything you want
--hostgroup This is used for creating tabs and connecting to them for hosts you specify in Dolphie's config file under a hostgroup section. As an example, you'll have a section called [cluster1] then below it will be listed each host on a new line in the format key=host where key can be anything you want. Hosts support optional port in the format host:port
--show-trxs-only Start with only showing threads that have an active transaction
--additional-columns Start with additional columns in Processlist panel
--historical-locks Always run the locks query so it can save historical data to its graph instead of only when the Locks panel is open. This query can be expensive in some environments
Expand Down
2 changes: 1 addition & 1 deletion dolphie/Modules/ArgumentParser.py
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ def _add_options(self):
"This is used for creating tabs and connecting to them for hosts you specify in"
" Dolphie's config file under a hostgroup section. As an example, you'll have a section"
" called [cluster1] then below it will be listed each host on a new line in the format"
" key=host where key can be anything you want"
" key=host where key can be anything you want. Hosts support optional port in the format host:port"
),
metavar="",
)
Expand Down
6 changes: 4 additions & 2 deletions dolphie/Modules/TabManager.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ class Tab:

topbar: TopBar = None
main_container: VerticalScroll = None
metric_graph_tabs: TabbedContent = None
loading_indicator: LoadingIndicator = None
sparkline: Sparkline = None
panel_dashboard: Container = None
Expand Down Expand Up @@ -223,7 +224,7 @@ async def create_tab(self, tab_name: str, use_hostgroup: bool = False) -> Tab:
classes="panel_container dashboard",
),
Container(
TabbedContent(id=f"tabbed_content_{tab_id}", classes="metrics_tabbed_content"),
TabbedContent(id=f"metric_graph_tabs_{tab_id}", classes="metrics_tabbed_content"),
id=f"panel_graphs_{tab_id}",
classes="panel_container",
),
Expand Down Expand Up @@ -309,7 +310,7 @@ async def create_tab(self, tab_name: str, use_hostgroup: bool = False) -> Tab:
metric_tab_name = metric_instance.tab_name
graph_names = metric_instance.graphs

await self.app.query_one(f"#tabbed_content_{tab_id}", TabbedContent).add_pane(
await self.app.query_one(f"#metric_graph_tabs_{tab_id}", TabbedContent).add_pane(
TabPane(
tab_formatted_name,
Label(id=f"stats_{metric_tab_name}_{tab_id}", classes="stats_data"),
Expand Down Expand Up @@ -365,6 +366,7 @@ async def create_tab(self, tab_name: str, use_hostgroup: bool = False) -> Tab:
# Save references to the widgets in the tab
tab.topbar = self.app.query_one(TopBar)
tab.main_container = self.app.query_one(f"#main_container_{tab.id}", VerticalScroll)
tab.metric_graph_tabs = self.app.query_one(f"#metric_graph_tabs_{tab.id}", TabbedContent)
tab.loading_indicator = self.app.query_one(f"#loading_indicator_{tab.id}", LoadingIndicator)
tab.sparkline = self.app.query_one(f"#panel_dashboard_queries_qps_{tab.id}", Sparkline)
tab.panel_dashboard = self.app.query_one(f"#panel_dashboard_{tab.id}", Container)
Expand Down
44 changes: 22 additions & 22 deletions dolphie/Panels/replication_panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -241,16 +241,22 @@ def create_replication_table(tab: Tab, dashboard_table=False, replica: Replica =
replica_sbm = dolphie.replica_lag

speed = 0
lag = None
if replica_sbm is not None:
if replica_previous_replica_sbm and replica_sbm < replica_previous_replica_sbm:
speed = round((replica_previous_replica_sbm - replica_sbm) / dolphie.polling_latency)

if replica_sbm >= 20:
lag = "[red]%s" % "{:0>8}[/red]".format(str(timedelta(seconds=replica_sbm)))
elif replica_sbm >= 10:
lag = "[yellow]%s[/yellow]" % "{:0>8}".format(str(timedelta(seconds=replica_sbm)))
else:
lag = "[green]%s[/green]" % "{:0>8}".format(str(timedelta(seconds=replica_sbm)))
replica_lag = replica_sbm
if data.get("SQL_Delay"):
replica_lag -= data["SQL_Delay"]

lag_color = "green"
if replica_lag >= 20:
lag_color = "red"
elif replica_lag >= 10:
lag_color = "yellow"

lag = f"[{lag_color}]{str(timedelta(seconds=replica_lag)).zfill(8)}[/{lag_color}]"

data["Master_Host"] = dolphie.get_hostname(data["Master_Host"])
mysql_gtid_enabled = False
Expand Down Expand Up @@ -289,31 +295,25 @@ def create_replication_table(tab: Tab, dashboard_table=False, replica: Replica =
if not dashboard_table:
table.add_row("[label]User", "%s" % data["Master_User"])

if data["Slave_IO_Running"].lower() == "yes":
io_thread_running = "[green]Yes[/green]"
else:
io_thread_running = "[red]NO[/red]"

if data["Slave_SQL_Running"].lower() == "yes":
sql_thread_running = "[green]Yes[/green]"
else:
sql_thread_running = "[red]NO[/red]"

io_thread_running = "[green]Yes[/green]" if data.get("Slave_IO_Running").lower() == "yes" else "[red]NO[/red]"
sql_thread_running = "[green]Yes[/green]" if data.get("Slave_SQL_Running").lower() == "yes" else "[red]NO[/red]"
table.add_row(
"[label]Thread",
"[label]IO %s [label]SQL %s" % (io_thread_running, sql_thread_running),
f"[label]IO {io_thread_running} [label]SQL {sql_thread_running}",
)

lag_source = "Lag"
if replica_sbm_source:
lag_source = f"Lag ({replica_sbm_source})"
replication_delay = ""
if data["SQL_Delay"]:
delay_formatted = str(timedelta(seconds=data["SQL_Delay"])).zfill(8)
replication_delay = f"([dark_yellow]delayed by {delay_formatted}[/dark_yellow])"

if replica_sbm is None or data["Slave_SQL_Running"].lower() == "no":
lag_source = f"Lag ({replica_sbm_source})" if replica_sbm_source else "Lag"
if lag is None or data["Slave_SQL_Running"].lower() == "no":
table.add_row(f"[label]{lag_source}", "")
else:
table.add_row(
"[label]%s" % lag_source,
"%s [label]Speed[/label] %s" % (lag, speed),
"%s [label]Speed[/label] %s %s" % (lag, speed, replication_delay),
)

if dashboard_table:
Expand Down
2 changes: 1 addition & 1 deletion dolphie/Widgets/host_setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ class HostSetupModal(ModalScreen):
def __init__(
self,
host: str,
port: str,
port: int,
username: str,
password: str,
available_hosts: list,
Expand Down
99 changes: 46 additions & 53 deletions dolphie/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@
from sqlparse import format as sqlformat
from textual import events, on, work
from textual.app import App, ComposeResult
from textual.css.query import NoMatches
from textual.widgets import Switch, TabbedContent
from textual.worker import Worker, WorkerState, get_current_worker

Expand Down Expand Up @@ -74,6 +73,7 @@ def __init__(self, config: Config):
"white": "#e9e9e9",
"green": "#54efae",
"yellow": "#f6ff8f",
"dark_yellow": "#cad45f",
"red": "#fd8383",
"purple": "#b565f3",
"dark_gray": "#969aad",
Expand All @@ -98,11 +98,10 @@ async def run_worker_main(self, tab_id: int):
# Get our worker thread
tab.worker = get_current_worker()
tab.worker.name = tab_id
tab.worker_running = True

dolphie = tab.dolphie
try:
tab.worker_running = True

if not dolphie.main_db_connection.is_connected():
self.tab_manager.rename_tab(tab_id) # this will use dolphie.host instead of mysql_host
tab.update_topbar(f"[[white]CONNECTING[/white]] {dolphie.host}:{dolphie.port}")
Expand Down Expand Up @@ -208,16 +207,15 @@ async def run_worker_main(self, tab_id: int):
replication_lag=dolphie.replica_lag,
)

tab.worker_running = False
except ManualException as exception:
# This will set up the worker state change function below to trigger the
# host setup modal with the error
tab.worker_running = False

tab.worker_cancel_error = exception

await tab.disconnect()

tab.worker_running = False

async def on_worker_state_changed(self, event: Worker.StateChanged):
tab = self.tab_manager.get_tab(event.worker.name)
if not tab:
Expand Down Expand Up @@ -273,15 +271,17 @@ async def on_worker_state_changed(self, event: Worker.StateChanged):
# Skip this if the conditions are right
if len(self.screen_stack) > 1 or dolphie.pause_refresh:
tab.replicas_worker_timer = self.set_timer(
tab.dolphie.refresh_interval, partial(self.run_worker_replicas, tab.id)
dolphie.refresh_interval, partial(self.run_worker_replicas, tab.id)
)

return

if dolphie.panels.replication.visible and dolphie.replica_manager.available_replicas:
replication_panel.create_replica_panel(tab)

tab.replicas_worker_timer = self.set_timer(2, partial(self.run_worker_replicas, tab.id))
tab.replicas_worker_timer = self.set_timer(
dolphie.refresh_interval, partial(self.run_worker_replicas, tab.id)
)

@work(thread=True, group="replicas")
def run_worker_replicas(self, tab_id: int):
Expand Down Expand Up @@ -315,64 +315,57 @@ def run_worker_replicas(self, tab_id: int):
def refresh_screen(self, tab: Tab):
dolphie = tab.dolphie

try:
loading_indicator = tab.loading_indicator
if loading_indicator.display:
loading_indicator.display = False
tab.main_container.display = True

self.layout_graphs(tab)
loading_indicator = tab.loading_indicator
if loading_indicator.display:
loading_indicator.display = False
tab.main_container.display = True

if self.tab.id == tab.id:
tab.update_topbar()
self.layout_graphs(tab)

if dolphie.panels.dashboard.visible:
self.refresh_panel(tab, dolphie.panels.dashboard.name)
if self.tab.id == tab.id:
tab.update_topbar()

# Update the sparkline for queries per second
sparkline = tab.sparkline
sparkline_data = dolphie.metric_manager.metrics.dml.Queries.values
if not sparkline.display:
sparkline_data = [0]
sparkline.display = True
if dolphie.panels.dashboard.visible:
self.refresh_panel(tab, dolphie.panels.dashboard.name)

sparkline.data = sparkline_data
sparkline.refresh()
# Update the sparkline for queries per second
sparkline = tab.sparkline
sparkline_data = dolphie.metric_manager.metrics.dml.Queries.values
if not sparkline.display:
sparkline_data = [0]
sparkline.display = True

if dolphie.panels.processlist.visible:
self.refresh_panel(tab, dolphie.panels.processlist.name)
sparkline.data = sparkline_data
sparkline.refresh()

if dolphie.panels.replication.visible:
self.refresh_panel(tab, dolphie.panels.replication.name)
if dolphie.panels.processlist.visible:
self.refresh_panel(tab, dolphie.panels.processlist.name)

if dolphie.panels.locks.visible:
self.refresh_panel(tab, dolphie.panels.locks.name)
if dolphie.panels.replication.visible:
self.refresh_panel(tab, dolphie.panels.replication.name)

if dolphie.panels.ddl.visible:
self.refresh_panel(tab, dolphie.panels.ddl.name)
if dolphie.panels.locks.visible:
self.refresh_panel(tab, dolphie.panels.locks.name)

if dolphie.panels.graphs.visible:
graph_panel = self.query_one(f"#tabbed_content_{tab.id}", TabbedContent)
if dolphie.panels.ddl.visible:
self.refresh_panel(tab, dolphie.panels.ddl.name)

# Hide/show replication tab based on replication status
if dolphie.replication_status:
graph_panel.show_tab(f"graph_tab_replication_lag_{tab.id}")
else:
graph_panel.hide_tab(f"graph_tab_replication_lag_{tab.id}")
if dolphie.panels.graphs.visible:
# Hide/show replication tab based on replication status
if dolphie.replication_status:
tab.metric_graph_tabs.show_tab(f"graph_tab_replication_lag_{tab.id}")
else:
tab.metric_graph_tabs.hide_tab(f"graph_tab_replication_lag_{tab.id}")

# Refresh the graph(s) for the selected tab
self.update_graphs(graph_panel.get_pane(graph_panel.active).name)
# Refresh the graph(s) for the selected tab
self.update_graphs(tab.metric_graph_tabs.get_pane(tab.metric_graph_tabs.active).name)

# We take a snapshot of the processlist to be used for commands
# since the data can change after a key is pressed
dolphie.processlist_threads_snapshot = dolphie.processlist_threads.copy()
# We take a snapshot of the processlist to be used for commands
# since the data can change after a key is pressed
dolphie.processlist_threads_snapshot = dolphie.processlist_threads.copy()

# This denotes that we've gone through the first loop of the worker thread
dolphie.completed_first_loop = True
except NoMatches:
# This is thrown if a user toggles panels on and off and the display_* states aren't 1:1
# with worker thread/state change due to asynchronous nature of the worker thread
pass
# This denotes that we've gone through the first loop of the worker thread
dolphie.completed_first_loop = True

async def on_key(self, event: events.Key):
if len(self.screen_stack) > 1:
Expand Down

0 comments on commit 8e49f16

Please sign in to comment.