Skip to content

Commit 49555ce

Browse files
DannyStoll1Daniel Stollextrawurst
authored
perf: prevent repeated status fetches in large repos (#2824)
* perf: prevent repeated status fetches in large repos Replace time-based cache invalidation with a generation counter. The old `StatusParams` included a millisecond timestamp (tick) in its hash, causing the cache to invalidate on every UI tick. For large repos with millions of files, this led to repeated index loading and 5+ minute load times. This change uses a different strategy to manage cache invalidation: - Remove tick from StatusParams hash - Add generation counter that increments after each fetch completes - Include generation in cache hash This ensures: - No repeated fetches while one is already pending, making gitui usable in large repos - New fetch starts immediately after completion, keeping gitui responsive in small repos - External file changes will still be detected on the next fetch cycle * fix changelog --------- Co-authored-by: Daniel Stoll <dstoll@radix.trade> Co-authored-by: extrawurst <776816+extrawurst@users.noreply.github.com> Co-authored-by: extrawurst <mail@rusticorn.com>
1 parent 3cf7a81 commit 49555ce

2 files changed

Lines changed: 13 additions & 17 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
88
## Unreleased
99

1010
### Changed
11-
<<<<<<< feat/version-message
1211
* improve `gitui --version` message [[@hlsxx](https://github.com/hlsxx)] ([#2838](https://github.com/gitui-org/gitui/issues/2838))
13-
=======
1412
* rust msrv bumped to `1.88`
1513

1614
### Fixed
15+
* fix extremely slow status loading in large repositories by replacing time-based cache invalidation with generation counter [[@DannyStoll1](https://github.com/DannyStoll1)] ([#2823](https://github.com/gitui-org/gitui/issues/2823))
1716
* fix panic when renaming or updating remote URL with no remotes configured [[@xvchris](https://github.com/xvchris)] ([#2868](https://github.com/gitui-org/gitui/issues/2868))
18-
>>>>>>> master
1917

2018
## [0.28.0] - 2025-12-14
2119

asyncgit/src/status.rs

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,11 @@ use crossbeam_channel::Sender;
1010
use std::{
1111
hash::Hash,
1212
sync::{
13-
atomic::{AtomicUsize, Ordering},
13+
atomic::{AtomicU64, AtomicUsize, Ordering},
1414
Arc, Mutex,
1515
},
16-
time::{SystemTime, UNIX_EPOCH},
1716
};
1817

19-
fn current_tick() -> u128 {
20-
SystemTime::now()
21-
.duration_since(UNIX_EPOCH)
22-
.expect("time before unix epoch!")
23-
.as_millis()
24-
}
25-
2618
#[derive(Default, Hash, Clone)]
2719
pub struct Status {
2820
pub items: Vec<StatusItem>,
@@ -31,19 +23,17 @@ pub struct Status {
3123
///
3224
#[derive(Default, Hash, Copy, Clone, PartialEq, Eq)]
3325
pub struct StatusParams {
34-
tick: u128,
3526
status_type: StatusType,
3627
config: Option<ShowUntrackedFilesConfig>,
3728
}
3829

3930
impl StatusParams {
4031
///
41-
pub fn new(
32+
pub const fn new(
4233
status_type: StatusType,
4334
config: Option<ShowUntrackedFilesConfig>,
4435
) -> Self {
4536
Self {
46-
tick: current_tick(),
4737
status_type,
4838
config,
4939
}
@@ -59,6 +49,8 @@ pub struct AsyncStatus {
5949
sender: Sender<AsyncGitNotification>,
6050
pending: Arc<AtomicUsize>,
6151
repo: RepoPath,
52+
/// Counter that increments after each completed fetch.
53+
generation: Arc<AtomicU64>,
6254
}
6355

6456
impl AsyncStatus {
@@ -73,6 +65,7 @@ impl AsyncStatus {
7365
last: Arc::new(Mutex::new(Status::default())),
7466
sender,
7567
pending: Arc::new(AtomicUsize::new(0)),
68+
generation: Arc::new(AtomicU64::new(0)),
7669
}
7770
}
7871

@@ -97,12 +90,14 @@ impl AsyncStatus {
9790
return Ok(None);
9891
}
9992

100-
let hash_request = hash(&params);
93+
let generation = self.generation.load(Ordering::Relaxed);
94+
let hash_request = hash(&(params, generation));
10195

10296
log::trace!(
103-
"request: [hash: {}] (type: {:?})",
97+
"request: [hash: {}] (type: {:?}, gen: {})",
10498
hash_request,
10599
params.status_type,
100+
generation,
106101
);
107102

108103
{
@@ -118,6 +113,7 @@ impl AsyncStatus {
118113

119114
let arc_current = Arc::clone(&self.current);
120115
let arc_last = Arc::clone(&self.last);
116+
let arc_generation = Arc::clone(&self.generation);
121117
let sender = self.sender.clone();
122118
let arc_pending = Arc::clone(&self.pending);
123119
let status_type = params.status_type;
@@ -138,6 +134,8 @@ impl AsyncStatus {
138134
log::error!("fetch_helper: {e}");
139135
}
140136

137+
// Increment generation to invalidate cache for next request
138+
arc_generation.fetch_add(1, Ordering::Relaxed);
141139
arc_pending.fetch_sub(1, Ordering::Relaxed);
142140

143141
sender

0 commit comments

Comments
 (0)