fix(drivers): isolate concurrent install progress
This commit is contained in:
parent
e5c436b563
commit
31c7fcc16f
|
|
@ -1,5 +1,5 @@
|
|||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted, computed, watch, nextTick } from "vue";
|
||||
import { ref, onMounted, onUnmounted, computed, watch, nextTick, type Ref } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { Activity, ExternalLink, Cpu, FolderOpen, FolderSync, MemoryStick, Search, Square, Trash2, Download, RotateCcw, Loader2, RefreshCw, Check, Clock3, FileUp } from "@lucide/vue";
|
||||
import { Button } from "@/components/ui/button";
|
||||
|
|
@ -18,7 +18,7 @@ import type { JdbcDriverInfo, JdbcLocalBundleInfo, JdbcMavenBundleInfo, JdbcPlug
|
|||
import * as api from "@/lib/backend/api";
|
||||
import type { AgentDriverInfo, DriverRuntimeInfo, DriverRuntimeSummary, DriverStoreUsage, JavaRuntimeConfig } from "@/lib/backend/api";
|
||||
import { formatRuntimeBytes, formatRuntimeCpu, formatRuntimeUptime, runtimeHealthClass, runtimeStatusClass, runtimeStatusDotClass } from "@/lib/connection/driverRuntimePresentation";
|
||||
import { addDriverInstallQueue, driverInstallProgressPercent, isDriverInstallProgressTarget, removeDriverInstallQueue, takeNextDriverInstallQueue, type DriverInstallProgress } from "@/lib/connection/driverInstallProgressUi";
|
||||
import { addDriverInstallQueue, driverInstallProgressChannel, driverInstallProgressPercent, isDriverInstallProgressTarget, removeDriverInstallQueue, takeNextDriverInstallQueue, updateDriverInstallProgress, type DriverInstallProgress } from "@/lib/connection/driverInstallProgressUi";
|
||||
import { PRESTOSQL_DRIVER_DB_TYPE, prestoSqlBuiltinDriverRow, prestoSqlMavenBundle } from "@/lib/database/prestoSqlBuiltinDriver";
|
||||
import type { DriverStoreFocus } from "@/lib/connection/agentDriverInstallHint";
|
||||
import { isOfflineDriverPackage, webDriverImportAccept } from "@/lib/driverStore/driverImportSelection";
|
||||
|
|
@ -172,7 +172,8 @@ const upgradingTotal = ref(0);
|
|||
const queuedDriverInstalls = ref<string[]>([]);
|
||||
const reinstallingJre = ref<string | null>(null);
|
||||
const refreshing = ref(false);
|
||||
const progress = ref<DriverInstallProgress | null>(null);
|
||||
const agentProgress = ref<DriverInstallProgress | null>(null);
|
||||
const jdbcPluginProgress = ref<DriverInstallProgress | null>(null);
|
||||
const javaRuntimeConfig = ref<JavaRuntimeConfig>({ mode: "managed", custom_java_path: null });
|
||||
const customJavaPath = ref("");
|
||||
const savingJavaRuntime = ref(false);
|
||||
|
|
@ -187,7 +188,8 @@ const DRIVER_RUNTIME_POLL_MS = 5000;
|
|||
const OFFLINE_DRIVER_DOWNLOAD_URL = "https://dbxio.com/cn/drivers";
|
||||
|
||||
let unlisten: (() => void) | null = null;
|
||||
const lastProgressPercent = ref<number | null>(null);
|
||||
const lastAgentProgressPercent = ref<number | null>(null);
|
||||
const lastJdbcPluginProgressPercent = ref<number | null>(null);
|
||||
|
||||
const installedJres = computed(() => {
|
||||
const jreMap = new Map<string, boolean>();
|
||||
|
|
@ -199,8 +201,7 @@ const installedJres = computed(() => {
|
|||
return [...jreMap.entries()].map(([key, installed]) => ({ key, installed })).sort((a, b) => b.key.localeCompare(a.key));
|
||||
});
|
||||
|
||||
const progressText = computed(() => {
|
||||
const p = progress.value;
|
||||
function formatProgressText(p: DriverInstallProgress | null, includeBatch: boolean): string {
|
||||
if (!p) return "";
|
||||
if (p.step === "jre-extract") return t("driverStore.progressJreExtract");
|
||||
if (p.step === "jdbc-plugin-extract") return t("driverStore.progressJdbcPluginExtract");
|
||||
|
|
@ -209,21 +210,32 @@ const progressText = computed(() => {
|
|||
const pct = Math.round(((p.downloaded ?? 0) / p.total) * 100);
|
||||
const dl = formatSize(p.downloaded ?? 0);
|
||||
const total = formatSize(p.total);
|
||||
const prefix = upgradingAll.value && upgradingCurrent.value ? `[${upgradingIndex.value}/${upgradingTotal.value}] ${upgradingCurrent.value} - ` : "";
|
||||
const prefix = includeBatch && upgradingAll.value && upgradingCurrent.value ? `[${upgradingIndex.value}/${upgradingTotal.value}] ${upgradingCurrent.value} - ` : "";
|
||||
return `${prefix}${label} ${dl} / ${total} (${pct}%)`;
|
||||
});
|
||||
}
|
||||
|
||||
const progressNumber = computed(() => {
|
||||
const next = driverInstallProgressPercent(progress.value);
|
||||
const agentProgressText = computed(() => formatProgressText(agentProgress.value, true));
|
||||
const jdbcPluginProgressText = computed(() => formatProgressText(jdbcPluginProgress.value, false));
|
||||
|
||||
function progressNumber(progress: DriverInstallProgress | null, lastProgressPercent: Ref<number | null>): number | null {
|
||||
const next = driverInstallProgressPercent(progress);
|
||||
if (next !== null) {
|
||||
lastProgressPercent.value = next;
|
||||
}
|
||||
return next ?? lastProgressPercent.value;
|
||||
});
|
||||
}
|
||||
|
||||
function resetInstallProgress() {
|
||||
progress.value = null;
|
||||
lastProgressPercent.value = null;
|
||||
const agentProgressNumber = computed(() => progressNumber(agentProgress.value, lastAgentProgressPercent));
|
||||
const jdbcPluginProgressNumber = computed(() => progressNumber(jdbcPluginProgress.value, lastJdbcPluginProgressPercent));
|
||||
|
||||
function resetAgentInstallProgress() {
|
||||
agentProgress.value = null;
|
||||
lastAgentProgressPercent.value = null;
|
||||
}
|
||||
|
||||
function resetJdbcPluginInstallProgress() {
|
||||
jdbcPluginProgress.value = null;
|
||||
lastJdbcPluginProgressPercent.value = null;
|
||||
}
|
||||
|
||||
const downloadCacheBytes = computed(() => Number(driverStoreUsage.value?.download_cache_bytes || 0));
|
||||
|
|
@ -269,7 +281,7 @@ function isDriverProgressActive(dbType: string): boolean {
|
|||
return isDriverInstallProgressTarget(dbType, {
|
||||
installing: installing.value,
|
||||
upgradingAll: upgradingAll.value,
|
||||
progress: progress.value,
|
||||
progress: agentProgress.value,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -277,8 +289,12 @@ function driverRequiresJavaRuntime(driver: AgentDriverInfo): boolean {
|
|||
return driver.requires_java_runtime ?? Boolean(driver.jre);
|
||||
}
|
||||
|
||||
function progressTitle(fallback: string): string {
|
||||
return progressText.value || fallback;
|
||||
function agentProgressTitle(fallback: string): string {
|
||||
return agentProgressText.value || fallback;
|
||||
}
|
||||
|
||||
function jdbcPluginProgressTitle(fallback: string): string {
|
||||
return jdbcPluginProgressText.value || fallback;
|
||||
}
|
||||
|
||||
function isPrestoSqlBuiltinDriver(dbType: string): boolean {
|
||||
|
|
@ -392,7 +408,7 @@ async function installDriver(dbType: string) {
|
|||
async function runDriverInstall(dbType: string) {
|
||||
const label = driverLabel(dbType);
|
||||
installing.value = dbType;
|
||||
resetInstallProgress();
|
||||
resetAgentInstallProgress();
|
||||
try {
|
||||
if (isPrestoSqlBuiltinDriver(dbType)) {
|
||||
if (!jdbcPluginStatus.value?.installed || !jdbcPluginStatus.value.compatible) {
|
||||
|
|
@ -417,7 +433,7 @@ async function runDriverInstall(dbType: string) {
|
|||
toast(t("driverStore.driverInstallFailed", { label, error: e }));
|
||||
} finally {
|
||||
installing.value = null;
|
||||
resetInstallProgress();
|
||||
resetAgentInstallProgress();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -435,7 +451,7 @@ async function runQueuedDriverInstalls() {
|
|||
async function upgradeAll() {
|
||||
upgradingAll.value = true;
|
||||
queuedDriverInstalls.value = [];
|
||||
resetInstallProgress();
|
||||
resetAgentInstallProgress();
|
||||
try {
|
||||
const updatableDbTypes = drivers.value.filter((driver) => driver.update_available).map((driver) => driver.db_type);
|
||||
const blockers = await api.checkAgentUpdateBlockers(updatableDbTypes);
|
||||
|
|
@ -458,7 +474,7 @@ async function upgradeAll() {
|
|||
upgradingCurrent.value = "";
|
||||
upgradingIndex.value = 0;
|
||||
upgradingTotal.value = 0;
|
||||
resetInstallProgress();
|
||||
resetAgentInstallProgress();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -538,7 +554,7 @@ async function importOfflineZip() {
|
|||
}
|
||||
if (!selected) return;
|
||||
importingZip.value = true;
|
||||
resetInstallProgress();
|
||||
resetAgentInstallProgress();
|
||||
try {
|
||||
const count = await api.importAgentsFromZip(selected);
|
||||
await refreshAgents();
|
||||
|
|
@ -547,7 +563,7 @@ async function importOfflineZip() {
|
|||
toast(t("driverStore.offlineImportFailed", { error: e }));
|
||||
} finally {
|
||||
importingZip.value = false;
|
||||
resetInstallProgress();
|
||||
resetAgentInstallProgress();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -604,7 +620,7 @@ async function importDriverFile(driver: AgentDriverInfo) {
|
|||
|
||||
async function reinstallJre(jreKey: string) {
|
||||
reinstallingJre.value = jreKey;
|
||||
resetInstallProgress();
|
||||
resetAgentInstallProgress();
|
||||
try {
|
||||
await api.reinstallJre(jreKey);
|
||||
await refreshAgents();
|
||||
|
|
@ -613,7 +629,7 @@ async function reinstallJre(jreKey: string) {
|
|||
toast(t("driverStore.jreReinstallFailed", { jre: jreKey, error: e }));
|
||||
} finally {
|
||||
reinstallingJre.value = null;
|
||||
resetInstallProgress();
|
||||
resetAgentInstallProgress();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -949,7 +965,7 @@ async function loadJdbcPluginStatus() {
|
|||
async function installJdbcPlugin() {
|
||||
if (isInstallingJdbcPlugin.value) return;
|
||||
isInstallingJdbcPlugin.value = true;
|
||||
resetInstallProgress();
|
||||
resetJdbcPluginInstallProgress();
|
||||
try {
|
||||
jdbcPluginStatus.value = await api.installJdbcPlugin();
|
||||
emitDriverUpdateCount();
|
||||
|
|
@ -959,7 +975,7 @@ async function installJdbcPlugin() {
|
|||
toast(String(e?.message || e), 5000);
|
||||
} finally {
|
||||
isInstallingJdbcPlugin.value = false;
|
||||
resetInstallProgress();
|
||||
resetJdbcPluginInstallProgress();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -979,6 +995,7 @@ async function installJdbcPluginLocal() {
|
|||
}
|
||||
if (!selected) return;
|
||||
isInstallingJdbcPlugin.value = true;
|
||||
resetJdbcPluginInstallProgress();
|
||||
try {
|
||||
jdbcPluginStatus.value = await api.installJdbcPluginLocal(selected);
|
||||
emitDriverUpdateCount();
|
||||
|
|
@ -988,6 +1005,7 @@ async function installJdbcPluginLocal() {
|
|||
toast(String(e?.message || e), 5000);
|
||||
} finally {
|
||||
isInstallingJdbcPlugin.value = false;
|
||||
resetJdbcPluginInstallProgress();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1114,10 +1132,15 @@ onMounted(async () => {
|
|||
void forceRefresh().catch(() => undefined);
|
||||
|
||||
unlisten = await api.listenAgentInstallProgress((payload) => {
|
||||
if (payload.step === "done" || payload.step === "all-done") {
|
||||
progress.value = null;
|
||||
const incoming = payload as DriverInstallProgress;
|
||||
const channel = driverInstallProgressChannel(incoming);
|
||||
const jdbcProgressBelongsToPrestoSql = channel === "jdbc-plugin" && installing.value === PRESTOSQL_DRIVER_DB_TYPE && !isInstallingJdbcPlugin.value;
|
||||
if (jdbcProgressBelongsToPrestoSql) {
|
||||
// PrestoSQL is shown as a built-in driver but installs through the JDBC plugin pipeline.
|
||||
agentProgress.value = updateDriverInstallProgress(agentProgress.value, incoming, "jdbc-plugin");
|
||||
} else {
|
||||
progress.value = payload as DriverInstallProgress;
|
||||
agentProgress.value = updateDriverInstallProgress(agentProgress.value, incoming, "agent");
|
||||
jdbcPluginProgress.value = updateDriverInstallProgress(jdbcPluginProgress.value, incoming, "jdbc-plugin");
|
||||
}
|
||||
if (payload.db_type && payload.total_drivers) {
|
||||
upgradingCurrent.value = drivers.value.find((d) => d.db_type === payload.db_type)?.label ?? payload.db_type;
|
||||
|
|
@ -1128,7 +1151,7 @@ onMounted(async () => {
|
|||
// (step="done") so its "Update" button disappears immediately instead of
|
||||
// staying disabled until the whole batch completes (step="all-done").
|
||||
// Single-driver installs (upgradingAll=false) are refreshed by runDriverInstall.
|
||||
if (upgradingAll.value && payload.step === "done") {
|
||||
if (upgradingAll.value && payload.step === "done" && channel === "agent") {
|
||||
void refreshAgents();
|
||||
}
|
||||
});
|
||||
|
|
@ -1233,7 +1256,7 @@ watch(driverStoreTab, (tab) => {
|
|||
</span>
|
||||
<Check v-if="jre.installed" class="h-4 w-4 text-green-600" />
|
||||
<span v-else class="text-xs text-muted-foreground">{{ t("driverStore.notInstalled") }}</span>
|
||||
<DriverInstallProgressCircle v-if="reinstallingJre === jre.key" :percent="progressNumber" :title="progressTitle(jre.installed ? t('driverStore.reinstalling') : t('driverStore.installing'))" />
|
||||
<DriverInstallProgressCircle v-if="reinstallingJre === jre.key" :percent="agentProgressNumber" :title="agentProgressTitle(jre.installed ? t('driverStore.reinstalling') : t('driverStore.installing'))" />
|
||||
<Button v-else-if="!jre.installed" type="button" variant="default" size="sm" class="h-8 rounded-[6px] text-xs" :disabled="reinstallingJre !== null || installing !== null" @click="reinstallJre(jre.key)">
|
||||
<Download class="h-3.5 w-3.5 mr-1" />
|
||||
{{ t("driverStore.install") }}
|
||||
|
|
@ -1299,7 +1322,7 @@ watch(driverStoreTab, (tab) => {
|
|||
<Clock3 class="h-3 w-3 mr-1" />
|
||||
{{ t("driverStore.queued") }}
|
||||
</Button>
|
||||
<DriverInstallProgressCircle v-else-if="!driver.installed && isDriverProgressActive(driver.db_type)" :percent="progressNumber" :title="progressTitle(t('driverStore.installing'))" />
|
||||
<DriverInstallProgressCircle v-else-if="!driver.installed && isDriverProgressActive(driver.db_type)" :percent="agentProgressNumber" :title="agentProgressTitle(t('driverStore.installing'))" />
|
||||
<Button v-else-if="!driver.installed" size="sm" class="h-7 rounded-[6px] text-xs" :disabled="upgradingAll" @click="installDriver(driver.db_type)">
|
||||
<Download class="h-3 w-3 mr-1" />
|
||||
{{ t("driverStore.install") }}
|
||||
|
|
@ -1327,7 +1350,7 @@ watch(driverStoreTab, (tab) => {
|
|||
<Clock3 class="h-3 w-3 mr-1" />
|
||||
{{ t("driverStore.queued") }}
|
||||
</Button>
|
||||
<DriverInstallProgressCircle v-else-if="driver.installed && driver.update_available && isDriverProgressActive(driver.db_type)" :percent="progressNumber" :title="progressTitle(t('driverStore.updating'))" />
|
||||
<DriverInstallProgressCircle v-else-if="driver.installed && driver.update_available && isDriverProgressActive(driver.db_type)" :percent="agentProgressNumber" :title="agentProgressTitle(t('driverStore.updating'))" />
|
||||
<Button v-else-if="driver.installed && driver.update_available" size="sm" variant="outline" class="h-7 rounded-[6px] text-xs" :disabled="upgradingAll" @click="installDriver(driver.db_type)">
|
||||
{{ t("driverStore.update") }}
|
||||
</Button>
|
||||
|
|
@ -1365,7 +1388,7 @@ watch(driverStoreTab, (tab) => {
|
|||
<Clock3 class="h-3 w-3 mr-1" />
|
||||
{{ t("driverStore.queued") }}
|
||||
</Button>
|
||||
<DriverInstallProgressCircle v-else-if="!driver.installed && isDriverProgressActive(driver.db_type)" :percent="progressNumber" :title="progressTitle(t('driverStore.installing'))" />
|
||||
<DriverInstallProgressCircle v-else-if="!driver.installed && isDriverProgressActive(driver.db_type)" :percent="agentProgressNumber" :title="agentProgressTitle(t('driverStore.installing'))" />
|
||||
<Button v-else-if="!driver.installed" size="sm" class="h-7 rounded-[6px] text-xs" :disabled="upgradingAll" @click="installDriver(driver.db_type)">
|
||||
<Download class="h-3 w-3 mr-1" />
|
||||
{{ t("driverStore.install") }}
|
||||
|
|
@ -1393,7 +1416,7 @@ watch(driverStoreTab, (tab) => {
|
|||
<Clock3 class="h-3 w-3 mr-1" />
|
||||
{{ t("driverStore.queued") }}
|
||||
</Button>
|
||||
<DriverInstallProgressCircle v-else-if="driver.installed && driver.update_available && isDriverProgressActive(driver.db_type)" :percent="progressNumber" :title="progressTitle(t('driverStore.updating'))" />
|
||||
<DriverInstallProgressCircle v-else-if="driver.installed && driver.update_available && isDriverProgressActive(driver.db_type)" :percent="agentProgressNumber" :title="agentProgressTitle(t('driverStore.updating'))" />
|
||||
<Button v-else-if="driver.installed && driver.update_available" size="sm" variant="outline" class="h-7 rounded-[6px] text-xs" :disabled="upgradingAll" @click="installDriver(driver.db_type)">
|
||||
{{ t("driverStore.update") }}
|
||||
</Button>
|
||||
|
|
@ -1417,7 +1440,7 @@ watch(driverStoreTab, (tab) => {
|
|||
</p>
|
||||
</div>
|
||||
<div class="flex shrink-0 items-center gap-3">
|
||||
<DriverInstallProgressCircle v-if="isInstallingJdbcPlugin" :percent="progressNumber" :title="progressTitle(t('driverStore.progressDownloadJdbcPlugin'))" />
|
||||
<DriverInstallProgressCircle v-if="isInstallingJdbcPlugin" :percent="jdbcPluginProgressNumber" :title="jdbcPluginProgressTitle(t('driverStore.progressDownloadJdbcPlugin'))" />
|
||||
<span v-if="jdbcPluginStatus?.installed" class="text-xs" :class="jdbcPluginStatus.compatible ? 'text-green-600' : 'text-destructive'">
|
||||
{{
|
||||
jdbcPluginStatus.compatible
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ import { assertCompleteDatabaseCategories, databaseSelectionForCategory } from "
|
|||
import { normalizeRocketmqNamesrvAddr } from "@/lib/connection/rocketmqNamesrv";
|
||||
import { normalizeRabbitmqAddresses } from "@/lib/connection/rabbitmqAddresses";
|
||||
import { detectMqUiAuthKind, isMqAuthKindAllowedForSystem, type MqUiAuthKind } from "@/lib/connection/mqAuth";
|
||||
import { driverInstallProgressPercent, type DriverInstallProgress } from "@/lib/connection/driverInstallProgressUi";
|
||||
import { driverInstallProgressChannel, driverInstallProgressPercent, type DriverInstallProgress } from "@/lib/connection/driverInstallProgressUi";
|
||||
import { requiresSqlServerLegacyCompatibilityComponent, setSqlServerLegacyCompatibilityConfig, sqlServerUsesLegacyCompatibility, SQLSERVER_LEGACY_COMPATIBILITY_DRIVER_KEY } from "@/lib/connection/sqlServerLegacyCompatibility";
|
||||
import {
|
||||
ArrowLeft,
|
||||
|
|
@ -1309,6 +1309,7 @@ function setAgentInstallDialogOpen(value: boolean) {
|
|||
|
||||
function handleAgentInstallProgress(payload: DriverInstallProgress) {
|
||||
if (!agentInstallRunning.value || !agentInstallDriverKey.value) return;
|
||||
if (driverInstallProgressChannel(payload) !== "agent") return;
|
||||
if (payload.db_type && payload.db_type !== agentInstallDriverKey.value) return;
|
||||
if (payload.step === "done" || payload.step === "all-done") {
|
||||
agentInstallProgress.value = null;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
import { describe, expect, it } from "vitest";
|
||||
import { driverInstallProgressChannel, updateDriverInstallProgress, type DriverInstallProgress } from "@/lib/connection/driverInstallProgressUi";
|
||||
|
||||
describe("driver install progress channels", () => {
|
||||
it("classifies agent and JDBC plugin progress independently", () => {
|
||||
expect(driverInstallProgressChannel({ step: "driver", db_type: "mysql" })).toBe("agent");
|
||||
expect(driverInstallProgressChannel({ step: "jre-extract", db_type: "oracle" })).toBe("agent");
|
||||
expect(driverInstallProgressChannel({ step: "jdbc-plugin" })).toBe("jdbc-plugin");
|
||||
expect(driverInstallProgressChannel({ step: "jdbc-plugin-extract" })).toBe("jdbc-plugin");
|
||||
});
|
||||
|
||||
it("does not let concurrent progress overwrite the other channel", () => {
|
||||
const agentProgress: DriverInstallProgress = { step: "driver", db_type: "mysql", downloaded: 20, total: 100 };
|
||||
const jdbcProgress: DriverInstallProgress = { step: "jdbc-plugin", downloaded: 70, total: 100 };
|
||||
|
||||
expect(updateDriverInstallProgress(agentProgress, jdbcProgress, "agent")).toBe(agentProgress);
|
||||
expect(updateDriverInstallProgress(jdbcProgress, agentProgress, "jdbc-plugin")).toBe(jdbcProgress);
|
||||
});
|
||||
|
||||
it("allows a built-in driver backed by JDBC to consume JDBC progress explicitly", () => {
|
||||
const jdbcProgress: DriverInstallProgress = { step: "jdbc-plugin", downloaded: 40, total: 100 };
|
||||
|
||||
expect(updateDriverInstallProgress(null, jdbcProgress, "jdbc-plugin")).toBe(jdbcProgress);
|
||||
expect(updateDriverInstallProgress(null, jdbcProgress, "agent")).toBeNull();
|
||||
});
|
||||
|
||||
it("clears only the channel identified by a terminal event", () => {
|
||||
const agentProgress: DriverInstallProgress = { step: "driver", db_type: "mysql", downloaded: 100, total: 100 };
|
||||
const jdbcProgress: DriverInstallProgress = { step: "jdbc-plugin", downloaded: 50, total: 100 };
|
||||
const agentDone: DriverInstallProgress = { step: "done", db_type: "mysql" };
|
||||
|
||||
expect(updateDriverInstallProgress(agentProgress, agentDone, "agent")).toBeNull();
|
||||
expect(updateDriverInstallProgress(jdbcProgress, agentDone, "jdbc-plugin")).toBe(jdbcProgress);
|
||||
});
|
||||
|
||||
it("ignores legacy ambiguous done events so operation cleanup owns reset", () => {
|
||||
const agentProgress: DriverInstallProgress = { step: "driver", db_type: "mysql", downloaded: 80, total: 100 };
|
||||
const jdbcProgress: DriverInstallProgress = { step: "jdbc-plugin", downloaded: 80, total: 100 };
|
||||
const ambiguousDone: DriverInstallProgress = { step: "done" };
|
||||
|
||||
expect(updateDriverInstallProgress(agentProgress, ambiguousDone, "agent")).toBe(agentProgress);
|
||||
expect(updateDriverInstallProgress(jdbcProgress, ambiguousDone, "jdbc-plugin")).toBe(jdbcProgress);
|
||||
});
|
||||
});
|
||||
|
|
@ -11,6 +11,23 @@ export interface DriverInstallProgressTargetState {
|
|||
progress: DriverInstallProgress | null;
|
||||
}
|
||||
|
||||
export type DriverInstallProgressChannel = "agent" | "jdbc-plugin";
|
||||
|
||||
const AGENT_PROGRESS_STEPS = new Set(["driver", "jre", "jre-extract", "all-done"]);
|
||||
|
||||
export function driverInstallProgressChannel(progress: DriverInstallProgress): DriverInstallProgressChannel | null {
|
||||
if (progress.step === "jdbc-plugin" || progress.step === "jdbc-plugin-extract") return "jdbc-plugin";
|
||||
if (progress.db_type || AGENT_PROGRESS_STEPS.has(progress.step)) return "agent";
|
||||
// Legacy "done" events have no owner, so the operation promise must clear its own channel.
|
||||
return null;
|
||||
}
|
||||
|
||||
export function updateDriverInstallProgress(current: DriverInstallProgress | null, incoming: DriverInstallProgress, channel: DriverInstallProgressChannel): DriverInstallProgress | null {
|
||||
if (driverInstallProgressChannel(incoming) !== channel) return current;
|
||||
if (incoming.step === "done" || incoming.step === "all-done") return null;
|
||||
return incoming;
|
||||
}
|
||||
|
||||
export function driverInstallProgressPercent(progress: DriverInstallProgress | null): number | null {
|
||||
if (!progress?.total || progress.total <= 0) return null;
|
||||
const percent = Math.round(((progress.downloaded ?? 0) / progress.total) * 100);
|
||||
|
|
|
|||
|
|
@ -605,7 +605,7 @@ async fn install_agent_driver_with_batch(
|
|||
if let Some(local_jar) = find_local_agent_jar(db_type) {
|
||||
install_local_agent(am, db_type, local_jar)?;
|
||||
am.stop_daemon_by_key(db_type).await;
|
||||
progress(AgentProgressEvent::step("done"));
|
||||
progress(AgentProgressEvent::step("done").with_batch(Some(db_type), current, total_drivers));
|
||||
return Ok(());
|
||||
}
|
||||
Err(registry_err)
|
||||
|
|
@ -682,7 +682,7 @@ async fn install_local_agent_with_registry_jre(
|
|||
am.save_state(&local_state)?;
|
||||
}
|
||||
am.stop_daemon_by_key(db_type).await;
|
||||
progress(AgentProgressEvent::step("done"));
|
||||
progress(AgentProgressEvent::step("done").with_batch(Some(db_type), current, total_drivers));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -782,7 +782,7 @@ async fn install_agent_driver_from_registry(
|
|||
am.save_state(&local_state)?;
|
||||
am.stop_daemon_by_key(db_type).await;
|
||||
cleanup_driver_download_cache_after_success(am, db_type);
|
||||
progress(AgentProgressEvent::step("done"));
|
||||
progress(AgentProgressEvent::step("done").with_batch(Some(db_type), current, total_drivers));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -1882,7 +1882,8 @@ mod agent_registry_install_tests {
|
|||
let native_path = manager.driver_native_path(db_type);
|
||||
let cache_path =
|
||||
write_cached_driver_download(&manager, db_type, version, native_url, &native_path, native_bytes);
|
||||
let progress = |_| {};
|
||||
let events = std::sync::Mutex::new(Vec::new());
|
||||
let progress = |event| events.lock().unwrap().push(event);
|
||||
|
||||
install_agent_driver_from_registry(
|
||||
&manager,
|
||||
|
|
@ -1900,6 +1901,11 @@ mod agent_registry_install_tests {
|
|||
assert!(!cache_path.exists());
|
||||
assert!(!manager.driver_jar_path(db_type).exists());
|
||||
assert_eq!(manager.load_state().installed_drivers.get(db_type).unwrap().version, version);
|
||||
assert!(events
|
||||
.lock()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.any(|event| event.step == "done" && event.db_type.as_deref() == Some(db_type)));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
|
|
|||
Loading…
Reference in New Issue