mirror of
https://github.com/mikedilger/gossip.git
synced 2024-09-29 00:11:01 +00:00
Add ability to prune unused people
This commit is contained in:
parent
6a63451603
commit
4b847b48e9
@ -1500,6 +1500,12 @@ impl eframe::App for GossipUi {
|
||||
return wait_for_data_migration(self, ctx);
|
||||
}
|
||||
|
||||
// If database is being pruned, show that screen
|
||||
let optstatus = GLOBALS.prune_status.read();
|
||||
if let Some(status) = optstatus.as_ref() {
|
||||
return wait_for_prune(self, ctx, status);
|
||||
}
|
||||
|
||||
// Wizard does its own panels
|
||||
if let Page::Wizard(wp) = self.page {
|
||||
return wizard::update(self, ctx, frame, wp);
|
||||
@ -2435,3 +2441,20 @@ fn wait_for_data_migration(app: &mut GossipUi, ctx: &Context) {
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
fn wait_for_prune(app: &mut GossipUi, ctx: &Context, status: &str) {
|
||||
egui::CentralPanel::default()
|
||||
.frame({
|
||||
let frame = egui::Frame::central_panel(&app.theme.get_style());
|
||||
frame.inner_margin(egui::Margin {
|
||||
left: 20.0,
|
||||
right: 10.0,
|
||||
top: 10.0,
|
||||
bottom: 0.0,
|
||||
})
|
||||
})
|
||||
.show(ctx, |ui| {
|
||||
ui.heading("Please wait for the database prune to complete...");
|
||||
ui.label(status);
|
||||
});
|
||||
}
|
||||
|
@ -12,13 +12,13 @@ pub(super) fn update(app: &mut GossipUi, _ctx: &Context, _frame: &mut eframe::Fr
|
||||
ui.add_space(20.0);
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("How long to keep events")
|
||||
ui.label("When pruning events (below), How long to keep events")
|
||||
.on_hover_text("Events older than this will be deleted");
|
||||
ui.add(Slider::new(&mut app.unsaved_settings.prune_period_days, 7..=720).text("days"));
|
||||
});
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("How long to keep downloaded files")
|
||||
ui.label("When pruning cache (below), How long to keep downloaded files")
|
||||
.on_hover_text("Cached files older than this will be deleted");
|
||||
ui.add(
|
||||
Slider::new(&mut app.unsaved_settings.cache_prune_period_days, 7..=720).text("days"),
|
||||
@ -29,10 +29,15 @@ pub(super) fn update(app: &mut GossipUi, _ctx: &Context, _frame: &mut eframe::Fr
|
||||
let stored_settings = UnsavedSettings::load();
|
||||
if stored_settings == app.unsaved_settings {
|
||||
ui.add_space(20.0);
|
||||
if ui.button("Delete Old Events Now").on_hover_text("This will delete events older than the period specified above. but the LMDB files will continue consuming disk space. To compact them, copy withem with `mdb_copy -c` when gossip is not running.").clicked() {
|
||||
if ui.button("Delete Old Events Now").on_hover_text("This will delete events older than the period specified above. but the LMDB files will continue consuming disk space. To compact them, copy withem with `mdb_copy -c` when gossip is not running (see doc/DATABASE_MAINTENANCE.md).").clicked() {
|
||||
let _ = GLOBALS.to_overlord.send(ToOverlordMessage::PruneOldEvents);
|
||||
}
|
||||
|
||||
ui.add_space(20.0);
|
||||
if ui.button("Delete Unused People Now").on_hover_text("This will delete people without useful events and otherwise not referenced, but the LMDB files will continue consuming disk space. To compact them, copy withem with `mdb_copy -c` when gossip is not running (see doc/DATABASE_MAINTENANCE.md).").clicked() {
|
||||
let _ = GLOBALS.to_overlord.send(ToOverlordMessage::PruneUnusedPeople);
|
||||
}
|
||||
|
||||
ui.add_space(20.0);
|
||||
if ui.button("Delete Old Downloaded Files").on_hover_text("This will delete cache files with modification times older than the period specified above (unfortunately access times are often unavailable and/or unreliable). Note that this will eventually delete everybody's avatar, even if those are in heavy use.").clicked() {
|
||||
let _ = GLOBALS.to_overlord.send(ToOverlordMessage::PruneCache);
|
||||
|
@ -139,6 +139,9 @@ pub enum ToOverlordMessage {
|
||||
/// Calls [prune_old_events](crate::Overlord::prune_old_events)
|
||||
PruneOldEvents,
|
||||
|
||||
/// Calls [prune_unused_people](crate::Overlord::prune_unused_people)
|
||||
PruneUnusedPeople,
|
||||
|
||||
/// Calls [push_person_list](crate::Overlord::push_person_list)
|
||||
PushPersonList(PersonList),
|
||||
|
||||
|
@ -177,6 +177,9 @@ pub struct Globals {
|
||||
/// Current bookmarks, resolved into a Vec<Id> (updated by tasks)
|
||||
pub current_bookmarks: PRwLock<Vec<Id>>,
|
||||
pub recompute_current_bookmarks: Arc<Notify>,
|
||||
|
||||
/// If we are doing a long database prune, this will indicate the status
|
||||
pub prune_status: PRwLock<Option<String>>,
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
@ -248,6 +251,7 @@ lazy_static! {
|
||||
bookmarks: Arc::new(PRwLock::new(BookmarkList::empty())),
|
||||
current_bookmarks: PRwLock::new(Vec::new()),
|
||||
recompute_current_bookmarks: Arc::new(Notify::new()),
|
||||
prune_status: PRwLock::new(None),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -708,6 +708,9 @@ impl Overlord {
|
||||
ToOverlordMessage::PruneOldEvents => {
|
||||
Self::prune_old_events()?;
|
||||
}
|
||||
ToOverlordMessage::PruneUnusedPeople => {
|
||||
Self::prune_unused_people()?;
|
||||
}
|
||||
ToOverlordMessage::PushPersonList(person_list) => {
|
||||
self.push_person_list(person_list).await?;
|
||||
}
|
||||
@ -1878,7 +1881,7 @@ impl Overlord {
|
||||
GLOBALS
|
||||
.status_queue
|
||||
.write()
|
||||
.write("Pruning database, please be patient..".to_owned());
|
||||
.write("Pruning old events, please be patient..".to_owned());
|
||||
|
||||
let now = Unixtime::now();
|
||||
let then = now
|
||||
@ -1896,6 +1899,44 @@ impl Overlord {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Prune unused people
|
||||
pub fn prune_unused_people() -> Result<(), Error> {
|
||||
// Go offline
|
||||
let mut need_to_go_back_online: bool = false;
|
||||
if !GLOBALS.db().read_setting_offline() {
|
||||
need_to_go_back_online = true;
|
||||
GLOBALS.db().write_setting_offline(&true, None)?;
|
||||
let _ = GLOBALS.write_runstate.send(RunState::Offline);
|
||||
}
|
||||
|
||||
*GLOBALS.prune_status.write() = Some("pruning...".to_owned());
|
||||
|
||||
match GLOBALS.db().prune_unused_people() {
|
||||
Ok(count) => {
|
||||
GLOBALS.status_queue.write().write(format!(
|
||||
"Database has been pruned. {} people removed.",
|
||||
count
|
||||
));
|
||||
}
|
||||
Err(e) => {
|
||||
GLOBALS
|
||||
.status_queue
|
||||
.write()
|
||||
.write(format!("Database prune error: {e}"));
|
||||
}
|
||||
}
|
||||
|
||||
*GLOBALS.prune_status.write() = None;
|
||||
|
||||
// Go online
|
||||
if need_to_go_back_online {
|
||||
GLOBALS.db().write_setting_offline(&false, None)?;
|
||||
let _ = GLOBALS.write_runstate.send(RunState::Online);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Publish the user's specified PersonList
|
||||
pub async fn push_person_list(&mut self, list: PersonList) -> Result<(), Error> {
|
||||
let metadata = match GLOBALS.db().get_person_list_metadata(list)? {
|
||||
|
@ -1,6 +1,8 @@
|
||||
use super::Storage;
|
||||
use super::table::Table;
|
||||
use super::{PersonTable, Storage};
|
||||
use crate::error::Error;
|
||||
use nostr_types::{Event, Id, Unixtime};
|
||||
use crate::globals::GLOBALS;
|
||||
use nostr_types::{Event, Filter, Id, Unixtime};
|
||||
use std::collections::HashSet;
|
||||
|
||||
impl Storage {
|
||||
@ -102,4 +104,86 @@ impl Storage {
|
||||
|
||||
Ok(ids.len())
|
||||
}
|
||||
|
||||
/// Prune people that are not used:
|
||||
/// * No feed related events
|
||||
/// * less than 6 events
|
||||
/// * not in any lists
|
||||
/// * not petnamed
|
||||
/// * no valid nip05,
|
||||
///
|
||||
/// Returns number of people deleted
|
||||
pub fn prune_unused_people<'a>(&'a self) -> Result<usize, Error> {
|
||||
let mut txn = self.get_write_txn()?;
|
||||
|
||||
let ekinds = crate::enabled_event_kinds();
|
||||
let frkinds = crate::feed_related_event_kinds(true);
|
||||
|
||||
let mut filter = Filter::new();
|
||||
filter.kinds = ekinds;
|
||||
filter.limit = Some(6);
|
||||
|
||||
let mut count = 0;
|
||||
let loop_txn = self.env.read_txn()?;
|
||||
for person in PersonTable::iter(&loop_txn)? {
|
||||
// Keep if they are in a person list
|
||||
if !self.read_person_lists(&person.pubkey)?.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Keep if they have a petname
|
||||
if person.petname.is_some() {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Keep if they have a valid nip-05
|
||||
if person.nip05_valid {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Load up to 6 of their events
|
||||
filter.authors = vec![person.pubkey.into()];
|
||||
let events = match self.find_events_by_filter(&filter, |_| true) {
|
||||
Ok(events) => events,
|
||||
Err(_) => continue, // some error we can't handle right now
|
||||
};
|
||||
|
||||
// Keep people with at least 6 events
|
||||
if events.len() >= 6 {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Keep if any of their events is feed related
|
||||
if events.iter().any(|e| frkinds.contains(&e.kind)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
count += 1;
|
||||
*GLOBALS.prune_status.write() = Some(
|
||||
person
|
||||
.pubkey
|
||||
.as_hex_string()
|
||||
.get(0..10)
|
||||
.unwrap_or("?")
|
||||
.to_owned(),
|
||||
);
|
||||
|
||||
// Delete their events
|
||||
for event in &events {
|
||||
self.delete_event(event.id, Some(&mut txn))?;
|
||||
}
|
||||
|
||||
// Delete their person-relay records
|
||||
self.delete_person_relays(|pr| pr.pubkey == person.pubkey, Some(&mut txn))?;
|
||||
|
||||
// Delete their person record
|
||||
PersonTable::delete_record(person.pubkey, Some(&mut txn))?;
|
||||
}
|
||||
|
||||
tracing::info!("PRUNE: deleted {} records from people", count);
|
||||
|
||||
txn.commit()?;
|
||||
|
||||
Ok(count)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user