Skip to content

Commit

Permalink
Combine commands k & K for killing threads + a few style tweaks
Browse files Browse the repository at this point in the history
  • Loading branch information
charles-001 committed Dec 21, 2024
1 parent 56369d8 commit 1f1b703
Show file tree
Hide file tree
Showing 10 changed files with 123 additions and 101 deletions.
97 changes: 43 additions & 54 deletions dolphie/App.py
Original file line number Diff line number Diff line change
Expand Up @@ -568,7 +568,7 @@ def process_mysql_data(self, tab: Tab):
if dolphie.panels.processlist.visible or dolphie.record_for_replay:
dolphie.processlist_threads = ProcesslistPanel.fetch_data(tab)

if dolphie.innodb_cluster or dolphie.innodb_cluster_read_replica:
if dolphie.panels.replication.visible and (dolphie.innodb_cluster or dolphie.innodb_cluster_read_replica):
dolphie.main_db_connection.execute(MySQLQueries.get_clustersets)
dolphie.innodb_cluster_clustersets = dolphie.main_db_connection.fetchall()

Expand Down Expand Up @@ -1417,27 +1417,13 @@ def command_get_input(filter_data):

elif key == "k":

def command_get_input(data):
self.run_command_in_worker(key=key, dolphie=dolphie, additional_data=data)

self.app.push_screen(
CommandModal(
command=HotkeyCommands.thread_kill_by_id,
message="Kill Thread",
processlist_data=dolphie.processlist_threads_snapshot,
),
command_get_input,
)

elif key == "K":

def command_get_input(data):
self.run_command_in_worker(key=key, dolphie=dolphie, additional_data=data)

self.app.push_screen(
CommandModal(
command=HotkeyCommands.thread_kill_by_parameter,
message="Kill threads by parameter(s)",
message="Kill thread(s)",
processlist_data=dolphie.processlist_threads_snapshot,
),
command_get_input,
Expand Down Expand Up @@ -1876,20 +1862,11 @@ def show_thread_screen():
)

self.call_from_thread(show_command_screen)
elif key == "k":
thread_id = additional_data
try:
query = dolphie.build_kill_query(thread_id)
dolphie.secondary_db_connection.execute(query)

self.notify("Killed Thread ID [b highlight]%s[/b highlight]" % thread_id, severity="success")
except ManualException as e:
self.notify(e.reason, title="Error killing Thread ID", severity="error")

elif key == "K":

elif key == "k":
# Unpack the data from the modal
(
kill_by_id,
kill_by_username,
kill_by_host,
kill_by_age_range,
Expand All @@ -1899,37 +1876,49 @@ def show_thread_screen():
include_sleeping_queries,
) = additional_data

threads_killed = 0
commands_to_kill = ["Query", "Execute"]
if kill_by_id:
try:
query = dolphie.build_kill_query(kill_by_id)
dolphie.secondary_db_connection.execute(query)

if include_sleeping_queries:
commands_to_kill.append("Sleep")
self.notify(f"Killed Thread ID [b highlight]{kill_by_id}[/b highlight]", severity="success")
except ManualException as e:
self.notify(e.reason, title="Error killing Thread ID", severity="error")
else:
threads_killed = 0
commands_to_kill = ["Query", "Execute"]

# Make a copy of the threads snapshot to avoid modification during iteration
threads = dolphie.processlist_threads_snapshot.copy()
if include_sleeping_queries:
commands_to_kill.append("Sleep")

for thread_id, thread in threads.items():
thread: ProcesslistThread
try:
# Check if the thread matches all conditions
if (
thread.command in commands_to_kill
and (not kill_by_username or kill_by_username == thread.user)
and (not kill_by_host or kill_by_host == thread.host)
and (not kill_by_age_range or age_range_lower_limit <= thread.time <= age_range_upper_limit)
and (not kill_by_query_text or kill_by_query_text in thread.formatted_query.code)
):
query = dolphie.build_kill_query(thread_id)
dolphie.secondary_db_connection.execute(query)

threads_killed += 1
except ManualException as e:
self.notify(e.reason, title=f"Error Killing Thread ID {thread_id}", severity="error")
# Make a copy of the threads snapshot to avoid modification during next refresh polling
threads = dolphie.processlist_threads_snapshot.copy()

if threads_killed:
self.notify(f"Killed [highlight]{threads_killed}[/highlight] thread(s)")
else:
self.notify("No threads were killed")
for thread_id, thread in threads.items():
thread: ProcesslistThread
try:
# Check if the thread matches all conditions
if (
thread.command in commands_to_kill
and (not kill_by_username or kill_by_username == thread.user)
and (not kill_by_host or kill_by_host == thread.host)
and (
not kill_by_age_range
or age_range_lower_limit <= thread.time <= age_range_upper_limit
)
and (not kill_by_query_text or kill_by_query_text in thread.formatted_query.code)
):
query = dolphie.build_kill_query(thread_id)
dolphie.secondary_db_connection.execute(query)

threads_killed += 1
except ManualException as e:
self.notify(e.reason, title=f"Error Killing Thread ID {thread_id}", severity="error")

if threads_killed:
self.notify(f"Killed [highlight]{threads_killed}[/highlight] thread(s)")
else:
self.notify("No threads were killed")

elif key == "l":
deadlock = ""
Expand Down
1 change: 0 additions & 1 deletion dolphie/DataTypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,6 @@ def _get_formatted_number(self, number):
class HotkeyCommands:
show_thread = "show_thread"
thread_filter = "thread_filter"
thread_kill_by_id = "thread_kill_by_id"
thread_kill_by_parameter = "thread_kill_by_parameter"
variable_search = "variable_search"
rename_tab = "rename_tab"
Expand Down
14 changes: 8 additions & 6 deletions dolphie/Dolphie.py
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ def determine_distro_and_connection_source_alt(
version_comment = global_variables.get("version_comment", "").casefold()
basedir = global_variables.get("basedir", "").casefold()
aad_auth_only = global_variables.get("aad_auth_only")
aurora_version = global_variables.get("aurora_version")

# Identify MariaDB and its variants
aria_in_global_variables = any(variable.startswith("aria_") for variable in global_variables.keys())
Expand All @@ -262,7 +263,7 @@ def determine_distro_and_connection_source_alt(
return distro, conn_source

# Identify MySQL and its variants
if global_variables.get("aurora_version"):
if aurora_version:
return "Amazon Aurora", ConnectionSource.mysql
if "rdsdb" in basedir:
return "Amazon RDS (MySQL)", ConnectionSource.mysql
Expand All @@ -274,15 +275,16 @@ def determine_distro_and_connection_source_alt(
def build_kill_query(self, thread_id: int) -> str:
basedir = self.global_variables.get("basedir", "").casefold()
aad_auth_only = self.global_variables.get("aad_auth_only")
aurora_version = self.global_variables.get("aurora_version")

if "rdsdb" in basedir or self.global_variables.get("aurora_version"):
if "rdsdb" in basedir or aurora_version:
return f"CALL mysql.rds_kill({thread_id})"
elif aad_auth_only:
if aad_auth_only:
return f"CALL mysql.az_kill({thread_id})"
elif self.connection_source == ConnectionSource.proxysql:
if self.connection_source == ConnectionSource.proxysql:
return f"KILL CONNECTION {thread_id}"
else:
return f"KILL {thread_id}"

return f"KILL {thread_id}"

def collect_system_utilization(self):
if not self.enable_system_utilization:
Expand Down
10 changes: 7 additions & 3 deletions dolphie/Dolphie.tcss
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,13 @@ CommandList {
padding: 0;
}
& > .option-list--option-highlighted {
background: #171d2d;
color: #b7c7ee;
background: #171e2f;
text-style: bold;
}
& > .option-list--option-hover {
background: #171d2d;
color: #b7c7ee;
background: #171e2f;
text-style: bold;
}
}
Expand Down Expand Up @@ -361,11 +364,12 @@ Dropdown {
background: #151926;

& > .autocomplete--highlight-match {
background: #777500;
background: #384673;
}

& > .autocomplete--selection-cursor {
background: #283048;
color: #b7c7ee;
}
}

Expand Down
6 changes: 2 additions & 4 deletions dolphie/Modules/CommandManager.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,7 @@ def __init__(self):
"c": {"human_key": "c", "description": "Clear all filters set"},
"f": {"human_key": "f", "description": "Filter threads by field(s)"},
"E": {"human_key": "E", "description": "Export the processlist to a CSV file"},
"k": {"human_key": "k", "description": "Kill thread by its ID"},
"K": {"human_key": "K", "description": "Kill threads by parameter(s)"},
"k": {"human_key": "k", "description": "Kill thread(s)"},
"M": {"human_key": "M", "description": "Maximize a panel"},
"q": {"human_key": "q", "description": "Quit"},
"r": {"human_key": "r", "description": "Set the refresh interval"},
Expand Down Expand Up @@ -113,8 +112,7 @@ def __init__(self):
"c": {"human_key": "c", "description": "Clear all filters set"},
"f": {"human_key": "f", "description": "Filter threads by field(s)"},
"E": {"human_key": "E", "description": "Export the processlist to a CSV file"},
"k": {"human_key": "k", "description": "Kill thread by its ID"},
"K": {"human_key": "K", "description": "Kill threads by parameter(s)"},
"k": {"human_key": "k", "description": "Kill thread(s)"},
"M": {"human_key": "M", "description": "Maximize a panel"},
"q": {"human_key": "q", "description": "Quit"},
"r": {"human_key": "r", "description": "Set the refresh interval"},
Expand Down
4 changes: 2 additions & 2 deletions dolphie/Modules/MySQL.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,9 +191,9 @@ def execute(self, query, values=None, ignore_error=False):
)
return None

# Prefix all queries with dolphie so they can be identified in the processlist from other people
# Prefix all queries with Dolphie so they can be easily identified in the processlist from other people
if self.source != ConnectionSource.proxysql:
query = "/* dolphie */ " + query
query = "/* Dolphie */ " + query

for attempt_number in range(self.max_reconnect_attempts):
self.is_running_query = True
Expand Down
15 changes: 8 additions & 7 deletions dolphie/Widgets/AutoComplete.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,15 +253,16 @@ class Dropdown(Widget):
max-height: 12;
max-width: 1fr;
scrollbar-size-vertical: 1;
border-left: wide #384673;
}
Dropdown .autocomplete--highlight-match {
color: $accent-lighten-2;
text-style: bold;
}
Dropdown .autocomplete--selection-cursor {
background: $boost;
text-style: bold;
}
"""

Expand Down Expand Up @@ -325,11 +326,11 @@ def on_mount(self, event: events.Mount) -> None:
callback=self._input_value_changed,
)

self.watch(
self.input_widget,
attribute_name="cursor_position",
callback=self._input_cursor_position_changed,
)
# self.watch(
# self.input_widget,
# attribute_name="cursor_position",
# callback=self._input_cursor_position_changed,
# )

# TODO: Having to use scroll_target here because scroll_y doesn't fire.
# Will also probably need separate callbacks for x and y.
Expand Down Expand Up @@ -427,7 +428,7 @@ def reposition(
x, y, width, height = self.input_widget.content_region
line_below_cursor = y + 1 + scroll_target_adjust_y

cursor_screen_position = x + (input_cursor_position - self.input_widget.view_position)
cursor_screen_position = self.app.cursor_position.x
self.styles.margin = (
line_below_cursor,
right,
Expand Down
Loading

0 comments on commit 1f1b703

Please sign in to comment.