fix: latency cache staleness, reduce duplicate probes, fix CI arch detection and nfpm compression
Release / Build aarch64-unknown-linux-gnu (push) Has been cancelled
Release / Build aarch64-apple-darwin (push) Has been cancelled
Release / Build aarch64-pc-windows-msvc (push) Has been cancelled
Release / Build x86_64-unknown-linux-gnu (push) Has been cancelled
Release / Build x86_64-apple-darwin (push) Has been cancelled
Release / Build x86_64-pc-windows-msvc (push) Has been cancelled
Release / Create GitHub Release (push) Has been cancelled
Release / Build aarch64-unknown-linux-gnu (push) Has been cancelled
Release / Build aarch64-apple-darwin (push) Has been cancelled
Release / Build aarch64-pc-windows-msvc (push) Has been cancelled
Release / Build x86_64-unknown-linux-gnu (push) Has been cancelled
Release / Build x86_64-apple-darwin (push) Has been cancelled
Release / Build x86_64-pc-windows-msvc (push) Has been cancelled
Release / Create GitHub Release (push) Has been cancelled
This commit is contained in:
@@ -126,12 +126,9 @@ jobs:
|
|||||||
uses: Minionguyjpro/Inno-Setup-Action@v1.2.8
|
uses: Minionguyjpro/Inno-Setup-Action@v1.2.8
|
||||||
env:
|
env:
|
||||||
SHELL_VERSION: ${{ env.SHELL_VERSION }}
|
SHELL_VERSION: ${{ env.SHELL_VERSION }}
|
||||||
|
SSH_TARGET: ${{ matrix.target }}
|
||||||
with:
|
with:
|
||||||
path: sshell.iss
|
path: sshell.iss
|
||||||
options: >-
|
|
||||||
/DMyArch=${{ matrix.target == 'x86_64-pc-windows-msvc' && 'x64compatible' || 'arm64' }}
|
|
||||||
/DMyArchAllowed=${{ matrix.target == 'x86_64-pc-windows-msvc' && 'x64compatible' || 'arm64' }}
|
|
||||||
/DMyArchInstall64=${{ matrix.target == 'x86_64-pc-windows-msvc' && 'x64compatible' || 'arm64' }}
|
|
||||||
|
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
|
|||||||
@@ -19,10 +19,10 @@ contents:
|
|||||||
mode: 0755
|
mode: 0755
|
||||||
|
|
||||||
deb:
|
deb:
|
||||||
compression: gz
|
compression: gzip
|
||||||
|
|
||||||
rpm:
|
rpm:
|
||||||
compression: gz
|
compression: gzip
|
||||||
|
|
||||||
archlinux:
|
archlinux:
|
||||||
pkgbase: sshell
|
pkgbase: sshell
|
||||||
|
|||||||
+11
-1
@@ -16,9 +16,19 @@ pub enum LatencyStatus {
|
|||||||
Unknown,
|
Unknown,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A cached probe result with a timestamp.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct CacheEntry {
|
||||||
|
pub status: LatencyStatus,
|
||||||
|
pub checked_at: Instant,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// How long before a cached entry is considered stale and re-probed.
|
||||||
|
pub const STALE_SECS: u64 = 30;
|
||||||
|
|
||||||
/// Shared latency cache keyed by "host:port" strings.
|
/// Shared latency cache keyed by "host:port" strings.
|
||||||
/// Background threads write, draw reads.
|
/// Background threads write, draw reads.
|
||||||
pub type LatencyCache = Arc<Mutex<HashMap<String, LatencyStatus>>>;
|
pub type LatencyCache = Arc<Mutex<HashMap<String, CacheEntry>>>;
|
||||||
|
|
||||||
/// Perform a TCP connect to measure latency.
|
/// Perform a TCP connect to measure latency.
|
||||||
/// Timeout is 3 seconds. Called from spawned threads.
|
/// Timeout is 3 seconds. Called from spawned threads.
|
||||||
|
|||||||
+47
-11
@@ -8,7 +8,6 @@ use crossterm::{
|
|||||||
terminal::{EnterAlternateScreen, LeaveAlternateScreen, disable_raw_mode, enable_raw_mode},
|
terminal::{EnterAlternateScreen, LeaveAlternateScreen, disable_raw_mode, enable_raw_mode},
|
||||||
};
|
};
|
||||||
use ratatui::{Terminal, backend::CrosstermBackend};
|
use ratatui::{Terminal, backend::CrosstermBackend};
|
||||||
use std::collections::HashSet;
|
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
@@ -33,12 +32,12 @@ pub fn run() -> Result<()> {
|
|||||||
if app.session.should_quit {
|
if app.session.should_quit {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
spawn_latency_probes(&app);
|
||||||
if event::poll(Duration::from_millis(200))?
|
if event::poll(Duration::from_millis(200))?
|
||||||
&& let Event::Key(key) = event::read()?
|
&& let Event::Key(key) = event::read()?
|
||||||
&& key.kind == KeyEventKind::Press
|
&& key.kind == KeyEventKind::Press
|
||||||
{
|
{
|
||||||
handle_key(&mut app, key)?;
|
handle_key(&mut app, key)?;
|
||||||
spawn_latency_probes(&app);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,25 +89,62 @@ fn handle_key(app: &mut App, key: KeyEvent) -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn spawn_latency_probes(app: &App) {
|
fn spawn_latency_probes(app: &App) {
|
||||||
let cache = app.session.latency.lock().unwrap();
|
use crate::app::latency::{CacheEntry, STALE_SECS};
|
||||||
let existing: HashSet<String> = cache.keys().cloned().collect();
|
use std::time::Instant;
|
||||||
drop(cache);
|
|
||||||
|
|
||||||
|
let stale_duration = Duration::from_secs(STALE_SECS);
|
||||||
|
let now = Instant::now();
|
||||||
|
|
||||||
|
// Collect keys that need probing (missing or stale)
|
||||||
|
let cache = app.session.latency.lock().unwrap();
|
||||||
|
let mut to_probe: Vec<String> = Vec::new();
|
||||||
for (_, profile) in app.entries() {
|
for (_, profile) in app.entries() {
|
||||||
if let ConnectionType::Ssh { host, port, .. } = &profile.kind {
|
if let ConnectionType::Ssh { host, port, .. } = &profile.kind {
|
||||||
let key = format!("{host}:{port}");
|
let key = format!("{host}:{port}");
|
||||||
if existing.contains(&key) {
|
let needs_probe = match cache.get(&key) {
|
||||||
|
None => true,
|
||||||
|
Some(entry) => now.duration_since(entry.checked_at) >= stale_duration,
|
||||||
|
};
|
||||||
|
if needs_probe && !to_probe.contains(&key) {
|
||||||
|
to_probe.push(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
drop(cache);
|
||||||
|
|
||||||
|
for key in to_probe {
|
||||||
|
// Re-check under lock to avoid duplicate spawns
|
||||||
|
{
|
||||||
|
let cache = app.session.latency.lock().unwrap();
|
||||||
|
if let Some(entry) = cache.get(&key) {
|
||||||
|
if now.duration_since(entry.checked_at) < stale_duration {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Mark as "in-flight" by inserting a fresh entry
|
||||||
|
{
|
||||||
|
let mut cache = app.session.latency.lock().unwrap();
|
||||||
|
cache.insert(key.clone(), CacheEntry {
|
||||||
|
status: crate::app::latency::LatencyStatus::Unknown,
|
||||||
|
checked_at: now,
|
||||||
|
});
|
||||||
|
}
|
||||||
let cache_clone = app.session.latency.clone();
|
let cache_clone = app.session.latency.clone();
|
||||||
let host = host.clone();
|
let host_port = key.clone();
|
||||||
let port = *port;
|
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
let status = crate::app::latency::probe(&host, port);
|
let parts: Vec<&str> = host_port.splitn(2, ':').collect();
|
||||||
|
let (host, port) = match parts.as_slice() {
|
||||||
|
[h, p] => (*h, p.parse::<u16>().unwrap_or(22)),
|
||||||
|
_ => return,
|
||||||
|
};
|
||||||
|
let status = crate::app::latency::probe(host, port);
|
||||||
if let Ok(mut cache) = cache_clone.lock() {
|
if let Ok(mut cache) = cache_clone.lock() {
|
||||||
cache.insert(key, status);
|
cache.insert(host_port, CacheEntry {
|
||||||
|
status,
|
||||||
|
checked_at: Instant::now(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -269,7 +269,7 @@ fn connection_row(
|
|||||||
ConnectionType::Ssh { host, port, .. } => {
|
ConnectionType::Ssh { host, port, .. } => {
|
||||||
let key = format!("{host}:{port}");
|
let key = format!("{host}:{port}");
|
||||||
let cache = app.session.latency.lock().unwrap();
|
let cache = app.session.latency.lock().unwrap();
|
||||||
match cache.get(&key) {
|
match cache.get(&key).map(|e| &e.status) {
|
||||||
Some(LatencyStatus::Reachable { ms }) => {
|
Some(LatencyStatus::Reachable { ms }) => {
|
||||||
let text = format!("{ms} ms");
|
let text = format!("{ms} ms");
|
||||||
let color = if *ms < 100 {
|
let color = if *ms < 100 {
|
||||||
|
|||||||
+10
@@ -7,6 +7,16 @@
|
|||||||
#define MyAppURL "https://github.com/Rain-Bus/sshell"
|
#define MyAppURL "https://github.com/Rain-Bus/sshell"
|
||||||
#define MyAppExeName "sshell.exe"
|
#define MyAppExeName "sshell.exe"
|
||||||
|
|
||||||
|
#if GetEnv("SSH_TARGET") == "aarch64-pc-windows-msvc"
|
||||||
|
#define MyArch "arm64"
|
||||||
|
#define MyArchAllowed "arm64"
|
||||||
|
#define MyArchInstall64 "arm64"
|
||||||
|
#else
|
||||||
|
#define MyArch "x64compatible"
|
||||||
|
#define MyArchAllowed "x64compatible"
|
||||||
|
#define MyArchInstall64 "x64compatible"
|
||||||
|
#endif
|
||||||
|
|
||||||
[Setup]
|
[Setup]
|
||||||
AppId={{sshell-2024-1}
|
AppId={{sshell-2024-1}
|
||||||
AppName={#MyAppName}
|
AppName={#MyAppName}
|
||||||
|
|||||||
Reference in New Issue
Block a user