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
|
||||
env:
|
||||
SHELL_VERSION: ${{ env.SHELL_VERSION }}
|
||||
SSH_TARGET: ${{ matrix.target }}
|
||||
with:
|
||||
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
|
||||
uses: actions/upload-artifact@v4
|
||||
|
||||
@@ -19,10 +19,10 @@ contents:
|
||||
mode: 0755
|
||||
|
||||
deb:
|
||||
compression: gz
|
||||
compression: gzip
|
||||
|
||||
rpm:
|
||||
compression: gz
|
||||
compression: gzip
|
||||
|
||||
archlinux:
|
||||
pkgbase: sshell
|
||||
|
||||
+11
-1
@@ -16,9 +16,19 @@ pub enum LatencyStatus {
|
||||
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.
|
||||
/// 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.
|
||||
/// 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},
|
||||
};
|
||||
use ratatui::{Terminal, backend::CrosstermBackend};
|
||||
use std::collections::HashSet;
|
||||
use std::io;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::time::Duration;
|
||||
@@ -33,12 +32,12 @@ pub fn run() -> Result<()> {
|
||||
if app.session.should_quit {
|
||||
break;
|
||||
}
|
||||
spawn_latency_probes(&app);
|
||||
if event::poll(Duration::from_millis(200))?
|
||||
&& let Event::Key(key) = event::read()?
|
||||
&& key.kind == KeyEventKind::Press
|
||||
{
|
||||
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) {
|
||||
let cache = app.session.latency.lock().unwrap();
|
||||
let existing: HashSet<String> = cache.keys().cloned().collect();
|
||||
drop(cache);
|
||||
use crate::app::latency::{CacheEntry, STALE_SECS};
|
||||
use std::time::Instant;
|
||||
|
||||
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() {
|
||||
if let ConnectionType::Ssh { host, port, .. } = &profile.kind {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 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 host = host.clone();
|
||||
let port = *port;
|
||||
let host_port = key.clone();
|
||||
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() {
|
||||
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, .. } => {
|
||||
let key = format!("{host}:{port}");
|
||||
let cache = app.session.latency.lock().unwrap();
|
||||
match cache.get(&key) {
|
||||
match cache.get(&key).map(|e| &e.status) {
|
||||
Some(LatencyStatus::Reachable { ms }) => {
|
||||
let text = format!("{ms} ms");
|
||||
let color = if *ms < 100 {
|
||||
|
||||
+10
@@ -7,6 +7,16 @@
|
||||
#define MyAppURL "https://github.com/Rain-Bus/sshell"
|
||||
#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]
|
||||
AppId={{sshell-2024-1}
|
||||
AppName={#MyAppName}
|
||||
|
||||
Reference in New Issue
Block a user