fix(ssh): prevent duplicate SSH tunnels from concurrent pool creation (#891)
start_tunnel() and start_chain() had a check-then-act race condition: multiple concurrent callers could all see an empty cache and each spawn a new SSH tunnel. The tunnel that lost the insert race was orphaned, causing queries routed through it to fail. Use double-check locking with stale entry eviction: hold the lock for the cache lookup, release it during the slow SSH handshake, then re-check under the lock before inserting. If another caller already created the tunnel, abort the duplicate and return the existing port. Also check handle liveness on cache hits so that callers don't get a dead port back when the background tunnel task has exited. Add a matching re-check in the MongoDB connection pool creation path. Add idle_timeout_secs connection option (default 60s) to preempt server-side connection idle timeouts that cause "unexpected end of file" errors on pooled MongoDB connections. Reject MongoDB queries in the generic SQL execution path before any pool or session-key creation. Previously, a typo in a MongoDB shell command would fall through to executeMulti / execute_sql_statement, which called get_or_create_pool_for_session and leaked a session-scoped MongoDB Client (and SSH tunnel resources) before eventually returning "Use MongoDB-specific commands". Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
1 parent
32164d5aea
commit
171c7cd8d7
15 files changed
+111
-16
No files matched your search
@@ -122,6 +122,7 @@ const defaultForm = (): ConnectionForm => ({
|
||||
transport_layers: [],
|
||||
connect_timeout_secs: 5,
|
||||
query_timeout_secs: 30,
|
||||
idle_timeout_secs: 60,
|
||||
ssl: false,
|
||||
ca_cert_path: "",
|
||||
client_cert_path: "",
|
||||
@@ -530,6 +531,7 @@ watch(
|
||||
transport_layers: transportLayersForConfig(legacyConfig),
|
||||
connect_timeout_secs: config.connect_timeout_secs || 5,
|
||||
query_timeout_secs: config.query_timeout_secs ?? 30,
|
||||
idle_timeout_secs: config.idle_timeout_secs ?? 60,
|
||||
ssl: config.ssl || false,
|
||||
ca_cert_path: config.ca_cert_path || "",
|
||||
client_cert_path: config.client_cert_path || "",
|
||||
@@ -1039,6 +1041,8 @@ function connectionConfigForSubmit(id: string): ConnectionConfig {
|
||||
config.connect_timeout_secs = Number.isFinite(connectTimeout) && connectTimeout > 0 ? connectTimeout : 5;
|
||||
const queryTimeout = Number(config.query_timeout_secs);
|
||||
config.query_timeout_secs = Number.isFinite(queryTimeout) && queryTimeout >= 0 ? queryTimeout : 30;
|
||||
const idleTimeout = Number(config.idle_timeout_secs);
|
||||
config.idle_timeout_secs = Number.isFinite(idleTimeout) && idleTimeout >= 0 ? idleTimeout : 60;
|
||||
if (!config.one_time) config.one_time = undefined;
|
||||
if (config.db_type === "mongodb" && !mongoUseUrl.value) {
|
||||
config.connection_string = undefined;
|
||||
@@ -3053,6 +3057,17 @@ function openExternalUrl(url: string) {
|
||||
class="col-span-3"
|
||||
/>
|
||||
</div>
|
||||
<div v-show="form.db_type === 'mongodb'" class="grid grid-cols-4 items-center gap-4">
|
||||
<Label class="text-right text-xs">{{ t("connection.idleTimeout") }}</Label>
|
||||
<Input
|
||||
v-model.number="form.idle_timeout_secs"
|
||||
type="number"
|
||||
min="0"
|
||||
max="600"
|
||||
step="1"
|
||||
class="col-span-3"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
|
||||
@@ -273,6 +273,7 @@ export default {
|
||||
sshHopInvalidTimeout: "{hop}: SSH timeout must be between 1 and 300 seconds",
|
||||
connectTimeout: "Connection Timeout (seconds)",
|
||||
queryTimeout: "Query Timeout (seconds)",
|
||||
idleTimeout: "Idle Timeout (seconds)",
|
||||
proxy: "Proxy",
|
||||
proxyEnable: "Connect database through proxy",
|
||||
proxyType: "Proxy Type",
|
||||
|
||||
@@ -254,6 +254,7 @@ export default {
|
||||
sshHopInvalidTimeout: "{hop}: el tiempo de espera SSH debe estar entre 1 y 300 segundos",
|
||||
connectTimeout: "Tiempo de espera de conexión (segundos)",
|
||||
queryTimeout: "Tiempo de espera de consulta (segundos)",
|
||||
idleTimeout: "Tiempo de espera inactivo (segundos)",
|
||||
proxy: "Proxy",
|
||||
proxyEnable: "Conectar la base de datos mediante proxy",
|
||||
proxyType: "Tipo de proxy",
|
||||
|
||||
@@ -265,6 +265,7 @@ export default {
|
||||
sshHopInvalidTimeout: "{hop}: Il timeout SSH deve essere compreso tra 1 e 300 secondi",
|
||||
connectTimeout: "Timeout Connessione (secondi)",
|
||||
queryTimeout: "Timeout Query (secondi)",
|
||||
idleTimeout: "Timeout Inattività (secondi)",
|
||||
proxy: "Proxy",
|
||||
proxyEnable: "Connetti database tramite proxy",
|
||||
proxyType: "Tipo Proxy",
|
||||
|
||||
@@ -264,6 +264,7 @@ export default {
|
||||
sshHopInvalidTimeout: "{hop}: o timeout SSH deve estar entre 1 e 300 segundos",
|
||||
connectTimeout: "Timeout de Conexão (segundos)",
|
||||
queryTimeout: "Timeout de Consulta (segundos)",
|
||||
idleTimeout: "Timeout de Inatividade (segundos)",
|
||||
proxy: "Proxy",
|
||||
proxyEnable: "Conectar ao banco de dados via proxy",
|
||||
proxyType: "Tipo de Proxy",
|
||||
|
||||
@@ -269,6 +269,7 @@ export default {
|
||||
sshHopInvalidTimeout: "{hop}:SSH 超时时间必须在 1 到 300 秒之间",
|
||||
connectTimeout: "连接超时(秒)",
|
||||
queryTimeout: "查询超时(秒)",
|
||||
idleTimeout: "空闲超时(秒)",
|
||||
proxy: "代理",
|
||||
proxyEnable: "通过代理连接数据库",
|
||||
proxyType: "代理类型",
|
||||
|
||||
@@ -258,6 +258,7 @@ export default {
|
||||
sshHopInvalidTimeout: "{hop}:SSH 逾時時間必須在 1 到 300 秒之間",
|
||||
connectTimeout: "連線逾時(秒)",
|
||||
queryTimeout: "查詢逾時(秒)",
|
||||
idleTimeout: "閒置逾時(秒)",
|
||||
proxy: "代理伺服器",
|
||||
proxyEnable: "藉由代理伺服器連線資料庫",
|
||||
proxyType: "代理伺服器類型",
|
||||
|
||||
@@ -305,6 +305,7 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
transport_layers: Array.isArray(config.transport_layers) ? config.transport_layers : [],
|
||||
connect_timeout_secs: config.connect_timeout_secs || 5,
|
||||
query_timeout_secs: config.query_timeout_secs ?? 30,
|
||||
idle_timeout_secs: config.idle_timeout_secs ?? 60,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -75,6 +75,7 @@ export interface ConnectionConfig {
|
||||
transport_layers?: TransportLayerConfig[];
|
||||
connect_timeout_secs?: number;
|
||||
query_timeout_secs?: number;
|
||||
idle_timeout_secs?: number;
|
||||
ssl?: boolean;
|
||||
ca_cert_path?: string;
|
||||
client_cert_path?: string;
|
||||
|
||||
Reference in new issue
Block a user