Track active projects in metrics
An active project is defined as a project where there has been at least a buffer edit, a join request/response, or a follow update in the last minute.
This commit is contained in:
parent
6d93a41f40
commit
3a1d0dd692
5 changed files with 97 additions and 22 deletions
|
@ -9,6 +9,7 @@ use std::{
|
|||
mem,
|
||||
path::{Path, PathBuf},
|
||||
str,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
use tracing::instrument;
|
||||
|
||||
|
@ -41,6 +42,8 @@ pub struct Project {
|
|||
pub active_replica_ids: HashSet<ReplicaId>,
|
||||
pub worktrees: BTreeMap<u64, Worktree>,
|
||||
pub language_servers: Vec<proto::LanguageServer>,
|
||||
#[serde(skip)]
|
||||
last_activity: Option<Instant>,
|
||||
}
|
||||
|
||||
#[derive(Default, Serialize)]
|
||||
|
@ -84,6 +87,7 @@ pub struct LeftProject {
|
|||
pub struct Metrics {
|
||||
pub connections: usize,
|
||||
pub registered_projects: usize,
|
||||
pub active_projects: usize,
|
||||
pub shared_projects: usize,
|
||||
}
|
||||
|
||||
|
@ -91,13 +95,17 @@ impl Store {
|
|||
pub fn metrics(&self) -> Metrics {
|
||||
let connections = self.connections.values().filter(|c| !c.admin).count();
|
||||
let mut registered_projects = 0;
|
||||
let mut active_projects = 0;
|
||||
let mut shared_projects = 0;
|
||||
for project in self.projects.values() {
|
||||
if let Some(connection) = self.connections.get(&project.host_connection_id) {
|
||||
if !connection.admin {
|
||||
registered_projects += 1;
|
||||
if !project.guests.is_empty() {
|
||||
shared_projects += 1;
|
||||
if project.is_active() {
|
||||
active_projects += 1;
|
||||
if !project.guests.is_empty() {
|
||||
shared_projects += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -106,6 +114,7 @@ impl Store {
|
|||
Metrics {
|
||||
connections,
|
||||
registered_projects,
|
||||
active_projects,
|
||||
shared_projects,
|
||||
}
|
||||
}
|
||||
|
@ -318,6 +327,7 @@ impl Store {
|
|||
active_replica_ids: Default::default(),
|
||||
worktrees: Default::default(),
|
||||
language_servers: Default::default(),
|
||||
last_activity: None,
|
||||
},
|
||||
);
|
||||
if let Some(connection) = self.connections.get_mut(&host_connection_id) {
|
||||
|
@ -338,6 +348,7 @@ impl Store {
|
|||
.get_mut(&project_id)
|
||||
.ok_or_else(|| anyhow!("no such project"))?;
|
||||
if project.host_connection_id == connection_id {
|
||||
project.last_activity = Some(Instant::now());
|
||||
let mut old_worktrees = mem::take(&mut project.worktrees);
|
||||
for worktree in worktrees {
|
||||
if let Some(old_worktree) = old_worktrees.remove(&worktree.id) {
|
||||
|
@ -460,6 +471,7 @@ impl Store {
|
|||
.get_mut(&project_id)
|
||||
.ok_or_else(|| anyhow!("no such project"))?;
|
||||
connection.requested_projects.insert(project_id);
|
||||
project.last_activity = Some(Instant::now());
|
||||
project
|
||||
.join_requests
|
||||
.entry(requester_id)
|
||||
|
@ -484,6 +496,8 @@ impl Store {
|
|||
let requester_connection = self.connections.get_mut(&receipt.sender_id)?;
|
||||
requester_connection.requested_projects.remove(&project_id);
|
||||
}
|
||||
project.last_activity = Some(Instant::now());
|
||||
|
||||
Some(receipts)
|
||||
}
|
||||
|
||||
|
@ -515,6 +529,7 @@ impl Store {
|
|||
receipts_with_replica_ids.push((receipt, replica_id));
|
||||
}
|
||||
|
||||
project.last_activity = Some(Instant::now());
|
||||
Some((receipts_with_replica_ids, project))
|
||||
}
|
||||
|
||||
|
@ -565,6 +580,8 @@ impl Store {
|
|||
}
|
||||
}
|
||||
|
||||
project.last_activity = Some(Instant::now());
|
||||
|
||||
Ok(LeftProject {
|
||||
host_connection_id: project.host_connection_id,
|
||||
host_user_id: project.host_user_id,
|
||||
|
@ -653,6 +670,25 @@ impl Store {
|
|||
.ok_or_else(|| anyhow!("no such project"))
|
||||
}
|
||||
|
||||
pub fn register_project_activity(
|
||||
&mut self,
|
||||
project_id: u64,
|
||||
connection_id: ConnectionId,
|
||||
) -> Result<()> {
|
||||
let project = self
|
||||
.projects
|
||||
.get_mut(&project_id)
|
||||
.ok_or_else(|| anyhow!("no such project"))?;
|
||||
if project.host_connection_id == connection_id
|
||||
|| project.guests.contains_key(&connection_id)
|
||||
{
|
||||
project.last_activity = Some(Instant::now());
|
||||
Ok(())
|
||||
} else {
|
||||
Err(anyhow!("no such project"))?
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_project(&self, project_id: u64, connection_id: ConnectionId) -> Result<&Project> {
|
||||
let project = self
|
||||
.projects
|
||||
|
@ -758,6 +794,13 @@ impl Store {
|
|||
}
|
||||
|
||||
impl Project {
|
||||
fn is_active(&self) -> bool {
|
||||
const ACTIVE_PROJECT_TIMEOUT: Duration = Duration::from_secs(60);
|
||||
self.last_activity.map_or(false, |last_activity| {
|
||||
last_activity.elapsed() < ACTIVE_PROJECT_TIMEOUT
|
||||
})
|
||||
}
|
||||
|
||||
pub fn guest_connection_ids(&self) -> Vec<ConnectionId> {
|
||||
self.guests.keys().copied().collect()
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue