File: C:/Program Files/MySQL/MySQL Workbench 8.0/modules/wb_admin_performance_dashboard.py
# Copyright (c) 2013, 2021, Oracle and/or its affiliates. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License, version 2.0,
# as published by the Free Software Foundation.
#
# This program is also distributed with certain software (including
# but not limited to OpenSSL) that is licensed under separate terms, as
# designated in a particular file or component or in included license
# documentation. The authors of MySQL hereby grant you an additional
# permission to link the program and your derivative works with the
# separately licensed software that they have included with MySQL.
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
# the GNU General Public License, version 2.0, for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import sys
import mforms
from workbench.graphics.charting import DBTimeLineGraph, DBSimpleCounter, DBRoundMeter, DBLevelMeter, DBImage, DBText
from workbench.graphics.canvas import Canvas, TextFigure
from workbench.graphics.cairo_utils import Context
from wb_admin_utils import weakcb, WbAdminTabBase, WbAdminValidationConnection
import re
from workbench.log import log_error
from workbench.utils import Version
from workbench.notifications import nc
from mforms import Color, ControlBackgroundColor, TextColor, TextBackgroundColor
class MyDict:
def __init__(self, d):
self.d = d
def __contains__(self, k):
print("contains", k)
return self.d.__contains__(k)
def __getitem__(self, k):
print("getit", k)
return self.d.__getitem__(k)
class RenderBox(mforms.PyDrawBox):
def __init__(self, parent):
mforms.PyDrawBox.__init__(self)
self.set_managed()
self.set_release_on_add()
self.parent = parent
self.canvas = None
self.set_instance(self)
self.drag_offset = None
self.drag_object = None
self.tooltip = None
self.offset = (0, 0)
self.current_pos = (0, 0)
self.variable_values = None
self.layouting_mode = False
def __del__(self):
if self.tooltip:
self.tooltip.close()
self.tooltip = None
def mouse_down(self, b, x, y):
x -= self.offset[0]
y -= self.offset[1]
if b == 0:
if self.layouting_mode:
self.drag_object = self.canvas.figure_at(x, y)
if self.drag_object:
self.drag_offset = x - self.drag_object.x, y - self.drag_object.y
def mouse_up(self, b, x, y):
x -= self.offset[0]
y -= self.offset[1]
if b == 0:
if self.drag_object:
xx = x - self.drag_offset[0]
yy = y - self.drag_offset[1]
xx -= xx % 2
yy -= yy % 2
self.drag_object.move(xx, yy)
self.drag_offset = None
self.drag_object = None
def mouse_move(self, x, y):
x -= self.offset[0]
y -= self.offset[1]
if self.drag_object:
xx = x - self.drag_offset[0]
yy = y - self.drag_offset[1]
xx -= xx % 2
yy -= yy % 2
self.drag_object.move(xx, yy)
self.current_pos = x, y
self.canvas.mouse_move(x, y)
def repaint(self, cr, x, y, w, h):
xoffs, yoffs = self.parent.relayout()
self.offset = xoffs, yoffs
c = Context(cr)
try:
self.canvas.repaint(c, xoffs, yoffs, w, h)
except Exception:
import traceback
log_error("Exception rendering dashboard: %s\n" % traceback.format_exc())
def add(self, figure):
self.canvas.add(figure)
figure.on_hover_in = self.handle_hover_in
figure.on_hover_out = self.handle_hover_out
def make_tooltip_text(self, figure, template):
try:
text = template % self.variable_values
except Exception:
return "--"
# find and evaluate all embedded ${expressions}
for m in re.findall("(\${[^}]*})", text):
value = eval(m[2:-1] % self.variable_values)
text = text.replace(m, str(value))
return text
def close_tooltip(self):
if self.tooltip:
self.tooltip.close()
self.tooltip = None
def handle_hover_out(self, fig, x, y):
self.close_tooltip()
def handle_hover_in(self, fig, x, y):
if self.tooltip:
self.tooltip.close()
self.tooltip = None
if not mforms.Form.main_form().is_active():
return
if fig and getattr(fig, 'hover_text_template', None):
text = self.make_tooltip_text(fig, fig.hover_text_template)
if text:
self.tooltip = mforms.newPopover(None, mforms.PopoverStyleTooltip)
fx = fig.x + self.offset[0] + fig.width + 4
fy = fig.y + self.offset[1] + fig.height / 2
xx, yy = self.client_to_screen(fx, fy)
box = mforms.newBox(False)
box.set_spacing(0)
t = ""
for line in text.split("\n"):
if line.startswith("*"):
if t:
if t.endswith("\n"):
t = t[:-1]
label = mforms.newLabel(t)
label.set_style(mforms.SmallStyle)
box.add(label, False, True)
t = ""
label = mforms.newLabel(line[1:].rstrip("\n"))
label.set_style(mforms.SmallBoldStyle)
box.add(label, False, True)
else:
t += line+"\n"
if t:
label = mforms.newLabel(t.rstrip("\n"))
label.set_style(mforms.SmallStyle)
box.add(label, False, True)
self.tooltip.set_size(max(box.get_preferred_width(), 100), max(box.get_preferred_height(), 50))
self.tooltip.set_content(box)
self.tooltip.add_close_callback(self.close_tooltip)
self.tooltip.show_and_track(self, xx, yy, mforms.StartRight)
class CDifferencePerSecond(object):
def __init__(self, expr):
self.expr = expr
self.reset()
def reset(self):
self.old_value = None
self.old_value_timestamp = None
def calculate(self, value, timestamp):
pass
def handle(self, values, timestamp):
result = None
if not self.expr:
return result
try:
value = eval(self.expr % values)
except Exception as e:
value = 0
if self.old_value and self.old_value_timestamp:
if timestamp > self.old_value_timestamp:
result = self.calculate(value, timestamp)
self.old_value = value
self.old_value_timestamp = timestamp
return result
class CSingleDifferencePerSecond(CDifferencePerSecond):
def __init__(self, expr):
super(CSingleDifferencePerSecond, self).__init__(expr)
def calculate(self, value, timestamp):
return float(value - self.old_value) / (timestamp - self.old_value_timestamp)
class CTupleDifferencePerSecond(CDifferencePerSecond):
def __init__(self, expr):
super(CTupleDifferencePerSecond, self).__init__(expr)
def calculate(self, value, timestamp):
result = []
for i in range(len(value)):
result.append(float(value[i] - self.old_value[i]) / (timestamp - self.old_value_timestamp))
return tuple(result)
class CRawValue(object):
def __init__(self, expr):
self.expr = expr
def handle(self, values, timestamp):
if not self.expr:
return None
try:
value = eval(self.expr % values)
except Exception as e:
value = 0
return value
class CMakeTuple(object):
def __init__(self, *items):
self.items = items
def handle(self, values, timestamp):
l = []
for i in self.items:
l.append(i.handle(values, timestamp))
return tuple(l)
READ_COLOR = (60/255.0, 178/255.0, 191/255.0)
WRITE_COLOR = (253/255.0, 138/255.0, 39/255.0)
GLOBAL_DASHBOARD_WIDGETS_NETWORK = \
[
(None, DBImage, (mforms.App.get().get_resource_path("dashboard_header_network_light.png"),), None, (None, None),
(0, 0, 0), (85, 5),
""),
(None, DBText, ("Statistics for network traffic sent and received\nby the MySQL Server over client connections.",), None, (None, None),
(0.4, 0.4, 0.4), (65, 72),
""),
(None, DBImage, (mforms.App.get().get_resource_path("dashboard_arrow_in_static.png"),), None, (None, None),
(0, 0, 0), (228, 196),
""),
(None, DBSimpleCounter, ("receiving\n%.2f %sB/s", True), None, (CSingleDifferencePerSecond, "%(Bytes_received)s"),
READ_COLOR, (200, 240),
"""Bytes Received
Number of bytes received by the MySQL server at the network level.
Total bytes received: %(Bytes_received)s"""),
("Incoming Network Traffic (Bytes/Second)", DBTimeLineGraph, ("%.1f %sB", True), None, (CSingleDifferencePerSecond, "%(Bytes_received)s"),
READ_COLOR, (30, 160),
None),
(None, DBImage, (mforms.App.get().get_resource_path("dashboard_arrow_out_static.png"),), None, (None, None),
(0, 0, 0), (228, 368),
""),
(None, DBSimpleCounter, ("sending\n%.2f %sB/s", True), None, (CSingleDifferencePerSecond, "%(Bytes_sent)s"),
WRITE_COLOR, (200, 410),
"""Bytes Sent
Number of bytes sent by the MySQL server at the network level.
Total bytes sent: %(Bytes_sent)s"""),
("Outgoing Network Traffic (Bytes/Second)", DBTimeLineGraph, ("%.1f %sB", True), None, (CSingleDifferencePerSecond, "%(Bytes_sent)s"),
WRITE_COLOR, (30, 330),
None),
("Client Connections (Total)", DBTimeLineGraph, ("%.1f %s", 1, True), None, (CRawValue, "%(Threads_connected)s"),
(124/255.0, 193/255.0, 80/255.0), (30, 500),
None),
(None, DBLevelMeter, tuple(), (CRawValue, "%(max_connections)s"), (CRawValue, "%(Threads_connected)s"),
(124/255.0, 193/255.0, 80/255.0), (200, 520),
"""Connections
Client connections/threads to the MySQL server.
Threads connected: %(Threads_connected)s
Threads running: %(Threads_running)s
Total connection attempts: %(Connections)s
Connection errors (accept): %(Connection_errors_accept)s
Connection errors (internal): %(Connection_errors_internal)s
Connection errors (max connections reached): %(Connection_errors_max_connections)s
Connection errors (peer address): %(Connection_errors_peer_address)s
Connection errors (select): %(Connection_errors_select)s
Connection errors (tcpwrap): %(Connection_errors_tcpwrap)s"""),
(None, DBImage, (mforms.App.get().get_resource_path("dashboard_separator.png"),), None, (None, None),
(0, 0, 0), (310, 120),
""),
]
GLOBAL_DASHBOARD_WIDGETS_MYSQL_PRE_80 = \
[
(None, DBImage, (mforms.App.get().get_resource_path("dashboard_header_mysql_light.png"),), None, (None, None),
(0, 0, 0), (380, 5),
""),
(None, DBText, ("Primary MySQL Server activity\nand performance statistics.",), None, (None, None),
(0.4, 0.4, 0.4), (376, 72),
""),
("Table Open Cache", DBRoundMeter, ("Efficiency",), None, (CRawValue, "%(Table_open_cache_hits)s/(%(Table_open_cache_hits)s+%(Table_open_cache_misses)s+0.0)"),
(124/255.0, 193/255.0, 80/255.0), (380, 150),
"""Table Open Cache
Cache for minimizing number of times MySQL
will open database tables when accessed.
Table open cache hits: %(Table_open_cache_hits)s
Table open cache misses: %(Table_open_cache_misses)s"""),
("SQL Statements Executed (#)", DBTimeLineGraph, ("%.1f %s", 3, True), None, (CTupleDifferencePerSecond, "(%(Com_select)s,%(Com_insert)s+%(Com_update)s+%(Com_delete)s,%(Com_create_db)s+%(Com_create_event)s+%(Com_create_function)s+%(Com_create_index)s+%(Com_create_procedure)s+%(Com_create_server)s+%(Com_create_table)s+%(Com_create_trigger)s+%(Com_create_udf)s+%(Com_create_user)s+%(Com_create_view)s+%(Com_alter_db)s+%(Com_alter_db_upgrade)s+%(Com_alter_event)s+%(Com_alter_function)s+%(Com_alter_procedure)s+%(Com_alter_server)s+%(Com_alter_table)s+%(Com_alter_tablespace)s+%(Com_alter_user)s+%(Com_drop_db)s+%(Com_drop_event)s+%(Com_drop_function)s+%(Com_drop_index)s+%(Com_drop_procedure)s+%(Com_drop_server)s+%(Com_drop_table)s+%(Com_drop_trigger)s+%(Com_drop_user)s+%(Com_drop_view)s)"),
[(255/255.0, 201/255.0, 2/255.0), (126/255.0, 142/255.0, 207/255.0), (194/255.0, 123/255.0, 206/255.0)], (350, 330),
None),
(None, DBSimpleCounter, ("SELECT\n%.0f %s/s", True), None, (CSingleDifferencePerSecond, "%(Com_select)s"),
(255/255.0, 201/255.0, 2/255.0), (350, 470),
"""SELECT Statements Executed
Total since start: %(Com_select)s"""),
(None, DBSimpleCounter, ("INSERT\n%.0f %s/s", True), None, (CSingleDifferencePerSecond, "%(Com_insert)s"),
(126/255.0, 142/255.0, 207/255.0), (350, 520),
"""INSERT Statements Executed
Total since start: %(Com_insert)s"""),
(None, DBSimpleCounter, ("UPDATE\n%.0f %s/s", True), None, (CSingleDifferencePerSecond, "%(Com_update)s"),
(126/255.0, 142/255.0, 207/255.0), (350, 560),
"""UPDATE Statements Executed
Total since start: %(Com_update)s"""),
(None, DBSimpleCounter, ("DELETE\n%.0f %s/s", True), None, (CSingleDifferencePerSecond, "%(Com_delete)s"),
(126/255.0, 142/255.0, 207/255.0), (350, 600),
"""DELETE Statements Executed
Total since start: %(Com_delete)s"""),
(None, DBSimpleCounter, ("CREATE\n%.0f %s/s", True), None, (CSingleDifferencePerSecond, "%(Com_create_db)s+%(Com_create_event)s+%(Com_create_function)s+%(Com_create_index)s+%(Com_create_procedure)s+%(Com_create_server)s+%(Com_create_table)s+%(Com_create_trigger)s+%(Com_create_udf)s+%(Com_create_user)s+%(Com_create_view)s"),
(194/255.0, 123/255.0, 206/255.0), (445, 520),
"""CREATE Statements Executed
Number of CREATE statements executed by the server (since server was started).
Create DB: %(Com_create_db)s
Create Event: %(Com_create_event)s
Create Function: %(Com_create_function)s
Create Index: %(Com_create_index)s
Create Procedure: %(Com_create_procedure)s
Create Server: %(Com_create_server)s
Create Table: %(Com_create_table)s
Create Trigger: %(Com_create_trigger)s
Create UDF: %(Com_create_udf)s
Create User: %(Com_create_user)s
Create View: %(Com_create_view)s"""),
(None, DBSimpleCounter, ("ALTER\n%.0f %s/s", True), None, (CSingleDifferencePerSecond, "%(Com_alter_db)s+%(Com_alter_db_upgrade)s+%(Com_alter_event)s+%(Com_alter_function)s+%(Com_alter_procedure)s+%(Com_alter_server)s+%(Com_alter_table)s+%(Com_alter_tablespace)s+%(Com_alter_user)s"),
(194/255.0, 123/255.0, 206/255.0), (445, 560),
"""ALTER Statements Executed
Number of ALTER statements executed by the server (since server was started).
Alter DB: %(Com_alter_db)s
Alter DB Upgrade: %(Com_alter_db_upgrade)s
Alter Event: %(Com_alter_event)s
Alter Function: %(Com_alter_function)s
Alter Procedure: %(Com_alter_procedure)s
Alter Server: %(Com_alter_server)s
Alter Table: %(Com_alter_table)s
Alter Tablespace: %(Com_alter_tablespace)s
Alter User: %(Com_alter_user)s"""),
(None, DBSimpleCounter, ("DROP\n%.0f %s/s", True), None, (CSingleDifferencePerSecond, "%(Com_drop_db)s+%(Com_drop_event)s+%(Com_drop_function)s+%(Com_drop_index)s+%(Com_drop_procedure)s+%(Com_drop_server)s+%(Com_drop_table)s+%(Com_drop_trigger)s+%(Com_drop_user)s+%(Com_drop_view)s"),
(194/255.0, 123/255.0, 206/255.0), (445, 600),
"""DROP Statements Executed
Number of DROP statements executed by the server (since server was started).
Drop DB: %(Com_drop_db)s
Drop Event: %(Com_drop_event)s
Drop Function: %(Com_drop_function)s
Drop Index: %(Com_drop_index)s
Drop Procedure: %(Com_drop_procedure)s
Drop Server: %(Com_drop_server)s
Drop Table: %(Com_drop_table)s
Drop Trigger: %(Com_drop_trigger)s
Drop User: %(Com_drop_user)s
Drop View: %(Com_drop_view)s"""),
(None, DBImage, (mforms.App.get().get_resource_path("dashboard_separator.png"),), None, (None, None),
(0, 0, 0), (570, 120),
""),
]
GLOBAL_DASHBOARD_WIDGETS_MYSQL_POST_80 = \
[
(None, DBImage, (mforms.App.get().get_resource_path("dashboard_header_mysql_light.png"),), None, (None, None),
(0, 0, 0), (380, 5),
""),
(None, DBText, ("Primary MySQL Server activity\nand performance statistics.",), None, (None, None),
(0.4, 0.4, 0.4), (376, 72),
""),
("Table Open Cache", DBRoundMeter, ("Efficiency",), None, (CRawValue, "%(Table_open_cache_hits)s/(%(Table_open_cache_hits)s+%(Table_open_cache_misses)s+0.0)"),
(124/255.0, 193/255.0, 80/255.0), (380, 150),
"""Table Open Cache
Cache for minimizing number of times MySQL
will open database tables when accessed.
Table open cache hits: %(Table_open_cache_hits)s
Table open cache misses: %(Table_open_cache_misses)s"""),
("SQL Statements Executed (#)", DBTimeLineGraph, ("%.1f %s", 3, True), None, (CTupleDifferencePerSecond, "(%(Com_select)s,%(Com_insert)s+%(Com_update)s+%(Com_delete)s,%(Com_create_db)s+%(Com_create_event)s+%(Com_create_function)s+%(Com_create_index)s+%(Com_create_procedure)s+%(Com_create_server)s+%(Com_create_table)s+%(Com_create_trigger)s+%(Com_create_udf)s+%(Com_create_user)s+%(Com_create_view)s+%(Com_create_role)s+%(Com_alter_db)s+%(Com_alter_event)s+%(Com_alter_function)s+%(Com_alter_procedure)s+%(Com_alter_server)s+%(Com_alter_table)s+%(Com_alter_tablespace)s+%(Com_alter_user)s+%(Com_alter_user_default_role)s+%(Com_drop_db)s+%(Com_drop_event)s+%(Com_drop_function)s+%(Com_drop_index)s+%(Com_drop_procedure)s+%(Com_drop_server)s+%(Com_drop_table)s+%(Com_drop_trigger)s+%(Com_drop_user)s+%(Com_drop_view)s+%(Com_drop_role)s)"),
[(255/255.0, 201/255.0, 2/255.0), (126/255.0, 142/255.0, 207/255.0), (194/255.0, 123/255.0, 206/255.0)], (350, 330),
None),
(None, DBSimpleCounter, ("SELECT\n%.0f %s/s", True), None, (CSingleDifferencePerSecond, "%(Com_select)s"),
(255/255.0, 201/255.0, 2/255.0), (350, 470),
"""SELECT Statements Executed
Total since start: %(Com_select)s"""),
(None, DBSimpleCounter, ("INSERT\n%.0f %s/s", True), None, (CSingleDifferencePerSecond, "%(Com_insert)s"),
(126/255.0, 142/255.0, 207/255.0), (350, 520),
"""INSERT Statements Executed
Total since start: %(Com_insert)s"""),
(None, DBSimpleCounter, ("UPDATE\n%.0f %s/s", True), None, (CSingleDifferencePerSecond, "%(Com_update)s"),
(126/255.0, 142/255.0, 207/255.0), (350, 560),
"""UPDATE Statements Executed
Total since start: %(Com_update)s"""),
(None, DBSimpleCounter, ("DELETE\n%.0f %s/s", True), None, (CSingleDifferencePerSecond, "%(Com_delete)s"),
(126/255.0, 142/255.0, 207/255.0), (350, 600),
"""DELETE Statements Executed
Total since start: %(Com_delete)s"""),
(None, DBSimpleCounter, ("CREATE\n%.0f %s/s", True), None, (CSingleDifferencePerSecond, "%(Com_create_db)s+%(Com_create_event)s+%(Com_create_function)s+%(Com_create_index)s+%(Com_create_procedure)s+%(Com_create_server)s+%(Com_create_table)s+%(Com_create_trigger)s+%(Com_create_udf)s+%(Com_create_user)s+%(Com_create_view)s+%(Com_create_role)s"),
(194/255.0, 123/255.0, 206/255.0), (445, 520),
"""CREATE Statements Executed
Number of CREATE statements executed by the server (since server was started).
Create DB: %(Com_create_db)s
Create Event: %(Com_create_event)s
Create Function: %(Com_create_function)s
Create Index: %(Com_create_index)s
Create Procedure: %(Com_create_procedure)s
Create Role: %(Com_create_role)s
Create Server: %(Com_create_server)s
Create Table: %(Com_create_table)s
Create Trigger: %(Com_create_trigger)s
Create UDF: %(Com_create_udf)s
Create User: %(Com_create_user)s
Create View: %(Com_create_view)s"""),
(None, DBSimpleCounter, ("ALTER\n%.0f %s/s", True), None, (CSingleDifferencePerSecond, "%(Com_alter_db)s+%(Com_alter_event)s+%(Com_alter_function)s+%(Com_alter_procedure)s+%(Com_alter_server)s+%(Com_alter_table)s+%(Com_alter_tablespace)s+%(Com_alter_user)s+%(Com_alter_user_default_role)s"),
(194/255.0, 123/255.0, 206/255.0), (445, 560),
"""ALTER Statements Executed
Number of ALTER statements executed by the server (since server was started).
Alter DB: %(Com_alter_db)s
Alter Event: %(Com_alter_event)s
Alter Function: %(Com_alter_function)s
Alter Procedure: %(Com_alter_procedure)s
Alter Server: %(Com_alter_server)s
Alter Table: %(Com_alter_table)s
Alter Tablespace: %(Com_alter_tablespace)s
Alter User: %(Com_alter_user)s
Alter User Default Role: %(Com_alter_user_default_role)s"""),
(None, DBSimpleCounter, ("DROP\n%.0f %s/s", True), None, (CSingleDifferencePerSecond, "%(Com_drop_db)s+%(Com_drop_event)s+%(Com_drop_function)s+%(Com_drop_index)s+%(Com_drop_procedure)s+%(Com_drop_server)s+%(Com_drop_table)s+%(Com_drop_trigger)s+%(Com_drop_user)s+%(Com_drop_view)s+%(Com_drop_role)s"),
(194/255.0, 123/255.0, 206/255.0), (445, 600),
"""DROP Statements Executed
Number of DROP statements executed by the server (since server was started).
Drop DB: %(Com_drop_db)s
Drop Event: %(Com_drop_event)s
Drop Function: %(Com_drop_function)s
Drop Index: %(Com_drop_index)s
Drop Procedure: %(Com_drop_procedure)s
Drop Role: %(Com_drop_role)s
Drop Server: %(Com_drop_server)s
Drop Table: %(Com_drop_table)s
Drop Trigger: %(Com_drop_trigger)s
Drop User: %(Com_drop_user)s
Drop View: %(Com_drop_view)s"""),
(None, DBImage, (mforms.App.get().get_resource_path("dashboard_separator.png"),), None, (None, None),
(0, 0, 0), (570, 120),
""),
]
GLOBAL_DASHBOARD_WIDGETS_INNODB = \
[
# InnoDB
(None, DBImage, (mforms.App.get().get_resource_path("dashboard_header_innodb_light.png"),), None, (None, None),
(0, 0, 0), (710, 5),
""),
(None, DBText, ("Overview of the InnoDB Buffer Pool and disk activity\ngenerated by the InnoDB storage engine.",), None, (None, None),
(0.4, 0.4, 0.4), (655, 72),
""),
# Buffer Pool
(None, DBSimpleCounter, ("read reqs.\n%.0f %s pages/s", True), None, (CSingleDifferencePerSecond, "%(Innodb_buffer_pool_read_requests)s"),
READ_COLOR, (610, 160),
"""InnoDB Buffer Pool Read Requests
The number of logical read requests InnoDB has done to the buffer pool.
Total: %(Innodb_buffer_pool_read_requests)s"""),
(None, DBSimpleCounter, ("write reqs.\n%.0f %s pages/s", True), None, (CSingleDifferencePerSecond, "%(Innodb_buffer_pool_write_requests)s"),
WRITE_COLOR, (610, 220),
"""InnoDB Buffer Pool Write Requests
The number of logical write requests InnoDB has done to the buffer pool.
Total: %(Innodb_buffer_pool_write_requests)s"""),
("InnoDB Buffer Pool", DBRoundMeter, ("Usage",), None, (CRawValue, "(%(Innodb_buffer_pool_bytes_data)s/%(Innodb_page_size)s)/(%(Innodb_buffer_pool_pages_total)s+0.0)"),
(124/255.0, 193/255.0, 80/255.0), (720, 150),
"""InnoDB Buffer Pool Usage Rate
How much of the InnoDB buffer pool is in use, from the amount allocated to it.
Usage Rate: ${round(((%(Innodb_buffer_pool_bytes_data)s/%(Innodb_page_size)s)/(%(Innodb_buffer_pool_pages_total)s+0.0))*100, 2)}%%
Total Pages Available: %(Innodb_buffer_pool_pages_total)s
Pages Used for Data: ${(%(Innodb_buffer_pool_bytes_data)s/%(Innodb_page_size)s)}
Pages Used Internally by InnoDB: ${%(Innodb_buffer_pool_pages_total)s-(%(Innodb_buffer_pool_bytes_data)s/%(Innodb_page_size)s)}
Pages Free: %(Innodb_buffer_pool_pages_free)s"""),
(None, DBSimpleCounter, ("disk reads\n%.0f %s #/s", True), None, (CSingleDifferencePerSecond, "%(Innodb_buffer_pool_reads)s"),
READ_COLOR, (890, 180),
"""InnoDB Buffer Pool Reads
The number of logical reads that InnoDB could not satisfy from the buffer pool, and had to read directly from the disk.
Total: %(Innodb_buffer_pool_reads)s"""),
("Redo Log", DBSimpleCounter, ("data written\n%.0f %sB/s", True), None, (CSingleDifferencePerSecond, "%(Innodb_os_log_written)s"),
WRITE_COLOR, (606, 330),
"""Bytes Written to InnoDB Redo Log
The number of bytes written to the InnoDB redo log files.
Total: %(Innodb_os_log_written)s"""),
(None, DBSimpleCounter, ("writes\n%.0f %s#/s", True), None, (CSingleDifferencePerSecond, "%(Innodb_log_writes)s"),
WRITE_COLOR, (606, 380),
"""Writes to InnoDB Redo Log
The number of physical writes to the InnoDB redo log file.
Total: %(Innodb_log_writes)s"""),
("Doublewrite Buffer", DBSimpleCounter, ("writes\n%.0f %s/s", True), None, (CSingleDifferencePerSecond, "%(Innodb_dblwr_writes)s"),
WRITE_COLOR, (606, 480),
"""Write Operations to InnoDB Doublewrite Buffer
The number of doublewrite operations that have been performed.
Total: %(Innodb_dblwr_writes)s"""),
# Storage
(None, DBImage, (mforms.App.get().get_resource_path("dashboard_arrow_out_static.png"),), None, (None, None),
(0, 0, 0), (934, 360),
""),
("InnoDB Disk Writes", DBTimeLineGraph, ("%.2f %sB", True), None, (CSingleDifferencePerSecond, "%(Innodb_data_written)s"),
WRITE_COLOR, (738, 330),
None),
(None, DBSimpleCounter, ("writing\n%.2f %sB/s", True), None, (CSingleDifferencePerSecond, "%(Innodb_data_written)s"),
WRITE_COLOR, (916, 410),
"""InnoDB Data Written
Total amount of data in bytes written in file operations by the InnoDB storage engine.
Total: %(Innodb_data_written)s"""),
(None, DBImage, (mforms.App.get().get_resource_path("dashboard_arrow_in_static.png"),), None, (None, None),
(0, 0, 0), (934, 535),
""),
("InnoDB Disk Reads", DBTimeLineGraph, ("%.2f %sB", True), None, (CSingleDifferencePerSecond, "%(Innodb_data_read)s"),
READ_COLOR, (738, 500),
None),
(None, DBSimpleCounter, ("reading\n%.2f %sB/s", True), None, (CSingleDifferencePerSecond, "%(Innodb_data_read)s"),
READ_COLOR, (916, 580),
"""InnoDB Data Read
Total amount of data in bytes read in file operations by the InnoDB storage engine.
Total: %(Innodb_data_read)s"""),
]
class WbAdminDashboard(WbAdminTabBase):
def __init__(self, ctrl_be, instance_info, main_view):
WbAdminTabBase.__init__(self, ctrl_be, instance_info, main_view)
nc.add_observer(self.updateColors, "GNColorsChanged")
self.min_server_version = (5, 6, 6)
self._refresh_tm = None
self.drawbox = None
self._form_deactivated_conn = None
self.add_validation(WbAdminValidationConnection(ctrl_be))
@classmethod
def wba_register(cls, admin_context):
admin_context.register_page(cls, "Performance", "Dashboard", "Dashboard", False)
@classmethod
def identifier(cls):
return "admin_dashboard"
def set_needs_repaint(self, x, y, w, h):
self.drawbox.set_needs_repaint()
def create_ui(self):
self._form_deactivated_conn = mforms.Form.main_form().add_deactivated_callback(self.form_deactivated)
self.content = mforms.newScrollPanel(0)
self.drawbox = RenderBox(self)
self.canvas = Canvas(self.set_needs_repaint)
if "linux" not in sys.platform:
color = Color.getSystemColor(ControlBackgroundColor)
self.canvas.set_background_color(color.red, color.green, color.blue)
self.drawbox.canvas = self.canvas
self.drawbox.set_size(1024, 700)
self.content.add(self.drawbox)
self.widgets = []
self.figures = []
self.last_refresh_time = None
self.drawbox.variable_values = self.ctrl_be.server_variables
server_version = Version.fromgrt(self.ctrl_be.target_version)
GLOBAL_DASHBOARD_WIDGETS = GLOBAL_DASHBOARD_WIDGETS_NETWORK + GLOBAL_DASHBOARD_WIDGETS_MYSQL_PRE_80 + GLOBAL_DASHBOARD_WIDGETS_INNODB
if server_version and server_version.is_supported_mysql_version_at_least(8, 0, 0):
GLOBAL_DASHBOARD_WIDGETS = GLOBAL_DASHBOARD_WIDGETS_NETWORK + GLOBAL_DASHBOARD_WIDGETS_MYSQL_POST_80 + GLOBAL_DASHBOARD_WIDGETS_INNODB
# create all widgets
for caption, wclass, args, init, (calc, calc_expr), color, pos, hover_text in GLOBAL_DASHBOARD_WIDGETS:
if caption:
fig = TextFigure(caption)
self.figures.append(fig)
fig.set_text_color(0, 0, 0)
fig.set_font_size(13)
fig.set_font_bold(True)
self.drawbox.add(fig)
fig.move(pos[0], pos[1] - 20)
w = wclass(calc(calc_expr) if calc else None, *args)
self.drawbox.add(w)
w.set_main_color(color)
w.move(*pos)
if hover_text:
w.hover_text_template = hover_text
if init:
init_calc, init_expr = init
w.init(init_calc(init_expr).handle(self.ctrl_be.server_variables, None))
self.widgets.append(w)
self.updateColors(None, None, None)
self.refresh()
self._refresh_tm = mforms.Utilities.add_timeout(self.ctrl_be.status_variable_poll_interval, self.refresh)
self.ctrl_be.add_me_for_event("server_started", self)
self.ctrl_be.add_me_for_event("server_stopped", self)
return self.content
def server_started_event(self):
for widget in self.widgets:
if not hasattr(widget, "calc"):
continue
if issubclass(type(widget.calc), CDifferencePerSecond):
widget.calc.reset()
#---------------------------------------------------------------------------
def server_stopped_event(self):
pass
def repaint(self):
self.drawbox.set_needs_repaint()
return True
def shutdown(self):
nc.remove_observer(self.updateColors)
if self._form_deactivated_conn:
self._form_deactivated_conn.disconnect()
self._form_deactivated_conn = None
if self._refresh_tm:
mforms.Utilities.cancel_timeout(self._refresh_tm)
self._refresh_tm = None
def refresh(self):
status_variables, timestamp = self.ctrl_be.status_variables, self.ctrl_be.status_variables_time
if self.last_refresh_time != timestamp:
for w in self.widgets:
if hasattr(w, 'process'):
w.process(status_variables, timestamp)
self.drawbox.variable_values.update(status_variables)
self.drawbox.set_needs_repaint()
return True
def form_deactivated(self):
if self.drawbox:
self.drawbox.close_tooltip()
def page_deactivated(self):
if self.drawbox:
self.drawbox.close_tooltip()
def relayout(self):
full_width = max(1024, self.content.get_width())
full_height = max(700, self.content.get_height())
if sys.platform.lower() == "darwin":
if self.drawbox.get_width() != full_width or self.drawbox.get_height() != full_height:
self.drawbox.set_size(full_width, full_height)
# return offset
return (full_width - 1024) / 2, (full_height - 700) / 2
def updateColors(self, name, sender, info):
if "linux" not in sys.platform:
color = Color.getSystemColor(ControlBackgroundColor)
self.canvas.set_background_color(color.red, color.green, color.blue)
textColor = Color.getSystemColor(TextColor)
backgroundColor = Color.getSystemColor(TextBackgroundColor)
for f in self.figures:
f.set_text_color(textColor.red, textColor.green, textColor.blue, textColor.alpha)
dark = mforms.App.get().isDarkModeActive()
for w in self.widgets:
if hasattr(w, "switch_image_mode"):
w.switch_image_mode(dark)
if hasattr(w, "set_fill_color"):
w.set_fill_color(backgroundColor.red, backgroundColor.green, backgroundColor.blue, backgroundColor.alpha)
if hasattr(w, "set_text_color"):
w.set_text_color(textColor.red, textColor.green, textColor.blue, textColor.alpha)