diff --git a/apps/desktop/src/components/admin/MySqlDashboard.vue b/apps/desktop/src/components/admin/MySqlDashboard.vue
index 2e90084603..c07eef3a62 100644
--- a/apps/desktop/src/components/admin/MySqlDashboard.vue
+++ b/apps/desktop/src/components/admin/MySqlDashboard.vue
@@ -9,7 +9,7 @@ import { useConnectionStore } from "@/stores/connectionStore";
import MetricCard from "@/components/common/MetricCard.vue";
import MetricLineChart from "@/components/chart/MetricLineChart.vue";
import * as api from "@/lib/backend/api";
-import { computeQps, computeRate, formatBytes, formatBytesPerSec, formatNumber, formatUptime, GLOBAL_STATUS_SQL, GLOBAL_VARIABLES_SQL, innodbBufferHitRatio, MAX_SAMPLES, parseStatusResult, statusEntries, statusNumber, type StatusSample } from "@/lib/database/mysqlServerStatus";
+import { computeQps, computeRate, formatBytes, formatBytesPerSec, formatNumber, formatRate, formatUptime, GLOBAL_STATUS_SQL, GLOBAL_VARIABLES_SQL, innodbBufferHitRatio, MAX_SAMPLES, parseStatusResult, statusEntries, statusNumber, type StatusSample } from "@/lib/database/mysqlServerStatus";
const props = defineProps<{
connectionId: string;
@@ -195,7 +195,7 @@ onUnmounted(stopAutoRefresh);
-
+
@@ -206,10 +206,10 @@ onUnmounted(stopAutoRefresh);
-
-
+
+
-
+
diff --git a/apps/desktop/src/lib/__tests__/database/mysqlServerStatus.spec.ts b/apps/desktop/src/lib/__tests__/database/mysqlServerStatus.spec.ts
index f35585cb9d..fefa9deab2 100644
--- a/apps/desktop/src/lib/__tests__/database/mysqlServerStatus.spec.ts
+++ b/apps/desktop/src/lib/__tests__/database/mysqlServerStatus.spec.ts
@@ -1,6 +1,6 @@
import { describe, expect, it } from "vitest";
import type { QueryResult } from "@/types/database";
-import { computeQps, computeRate, connectionSupportsServerDashboard, formatBytes, formatBytesPerSec, formatUptime, innodbBufferHitRatio, parseStatusResult, statusNumber, supportsServerDashboard, type StatusSample } from "@/lib/database/mysqlServerStatus";
+import { computeQps, computeRate, connectionSupportsServerDashboard, formatBytes, formatBytesPerSec, formatNumber, formatRate, formatUptime, innodbBufferHitRatio, parseStatusResult, statusNumber, supportsServerDashboard, type StatusSample } from "@/lib/database/mysqlServerStatus";
function statusResult(rows: [string, string][]): QueryResult {
return { columns: ["Variable_name", "Value"], rows, affected_rows: 0, execution_time_ms: 0 };
@@ -42,6 +42,14 @@ describe("computeRate", () => {
expect(computeRate(prev, curr, "Bytes_sent")).toBe(2000); // 4000 bytes / 2s
});
+ it("preserves a fractional connection rate over a longer sample interval", () => {
+ const prev = sample(0, { Connections: "100" });
+ const curr = sample(5000, { Connections: "101" });
+ const rate = computeRate(prev, curr, "Connections");
+ expect(rate).toBe(0.2);
+ expect(formatRate(rate)).toBe("0.2");
+ });
+
it("returns 0 on counter reset (decrease)", () => {
const prev = sample(1000, { Queries: "9000" });
const curr = sample(2000, { Queries: "10" });
@@ -75,6 +83,24 @@ describe("innodbBufferHitRatio", () => {
});
describe("formatters", () => {
+ it("formats rates with up to three fractional digits", () => {
+ expect(formatRate(0)).toBe("0");
+ expect(formatRate(0.2)).toBe("0.2");
+ expect(formatRate(0.125)).toBe("0.125");
+ expect(formatRate(1 / 3)).toBe("0.333");
+ expect(formatRate(1)).toBe("1");
+ expect(formatRate(1000)).toBe("1,000");
+ expect(formatRate(1234.5678)).toBe("1,234.568");
+ expect(formatRate(Number.NaN)).toBe("0");
+ expect(formatRate(Number.POSITIVE_INFINITY)).toBe("0");
+ expect(formatRate(Number.NEGATIVE_INFINITY)).toBe("0");
+ });
+
+ it("keeps integer metric formatting unchanged", () => {
+ expect(formatNumber(0.2)).toBe("0");
+ expect(formatNumber(1234.6)).toBe("1,235");
+ });
+
it("formats bytes and bytes/sec", () => {
expect(formatBytes(0)).toBe("0 B");
expect(formatBytes(1024)).toBe("1.0 KB");
diff --git a/apps/desktop/src/lib/database/mysqlServerStatus.ts b/apps/desktop/src/lib/database/mysqlServerStatus.ts
index 6db97498bc..5aefedc58c 100644
--- a/apps/desktop/src/lib/database/mysqlServerStatus.ts
+++ b/apps/desktop/src/lib/database/mysqlServerStatus.ts
@@ -110,6 +110,13 @@ export function formatNumber(value: number): string {
return Math.round(value).toLocaleString("en-US");
}
+const RATE_NUMBER_FORMATTER = new Intl.NumberFormat("en-US", { maximumFractionDigits: 3 });
+
+/** Cumulative-counter rates can be below 1/s, so preserve their fractional value. */
+export function formatRate(value: number): string {
+ return Number.isFinite(value) ? RATE_NUMBER_FORMATTER.format(value) : "0";
+}
+
const BYTE_UNITS = ["B", "KB", "MB", "GB", "TB"];
export function formatBytes(value: number): string {