mirror of
https://relay.ngit.dev/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit-grasp.git
synced 2026-10-05 15:08:24 +00:00
Integrate graph-based deletion algorithm into deletion flow
This commit is contained in:
@@ -551,30 +551,30 @@ impl Nip34WritePolicy {
|
||||
None
|
||||
};
|
||||
|
||||
// Query for all dependent events
|
||||
match crate::nostr::policy::query_dependent_events(
|
||||
// Determine which events should be deleted using graph-based algorithm
|
||||
match crate::nostr::policy::determine_events_to_delete(
|
||||
&self.ctx.database,
|
||||
&event_ids,
|
||||
&addresses,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(all_events) => {
|
||||
Ok(events_to_delete) => {
|
||||
tracing::info!(
|
||||
event_id = %event_id_str,
|
||||
author = %event.pubkey,
|
||||
event_count = event_ids.len(),
|
||||
address_count = addresses.len(),
|
||||
total_events = all_events.len(),
|
||||
"Processing deletion request - moving {} events to holding database",
|
||||
all_events.len()
|
||||
total_events = events_to_delete.len(),
|
||||
"Processing deletion request - moving {} events to holding database (graph-based)",
|
||||
events_to_delete.len()
|
||||
);
|
||||
|
||||
// Move events to holding database
|
||||
match crate::nostr::policy::move_to_holding_database(
|
||||
&self.ctx.database,
|
||||
holding_db,
|
||||
&all_events,
|
||||
&events_to_delete,
|
||||
&event.id,
|
||||
self.ctx.config.archive_retention_secs,
|
||||
archive_path,
|
||||
@@ -585,7 +585,7 @@ impl Nip34WritePolicy {
|
||||
tracing::info!(
|
||||
event_id = %event_id_str,
|
||||
moved_count = moved_count,
|
||||
"Successfully moved {} events to holding database",
|
||||
"Successfully moved {} events to holding database (graph-based)",
|
||||
moved_count
|
||||
);
|
||||
WritePolicyResult::Accept
|
||||
|
||||
@@ -11,6 +11,130 @@ use super::SharedDatabase;
|
||||
use crate::database::{DeletionMetadata, HoldingDatabase};
|
||||
use crate::git::archive::{archive_repository, create_archive_metadata};
|
||||
|
||||
/// Determine which events should be deleted using graph-based algorithm
|
||||
///
|
||||
/// This is the main integration point for the multi-maintainer deletion algorithm.
|
||||
/// It replaces the simple cascade deletion with a sophisticated graph-based approach:
|
||||
///
|
||||
/// 1. Query all potentially affected events (same as before)
|
||||
/// 2. Build EventGraph to understand dependencies
|
||||
/// 3. Re-evaluate events to determine which have alternative retention reasons
|
||||
/// 4. Traverse graph to mark events for deletion
|
||||
/// 5. Return only events that should actually be deleted
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `database` - Main database to query
|
||||
/// * `event_ids` - Event IDs being deleted
|
||||
/// * `addresses` - Addresses being deleted (format: `<kind>:<pubkey>:<d-tag>`)
|
||||
///
|
||||
/// # Returns
|
||||
/// Set of event IDs that should be DELETED (not all dependents, only those without retention reasons)
|
||||
pub async fn determine_events_to_delete(
|
||||
database: &SharedDatabase,
|
||||
event_ids: &[EventId],
|
||||
addresses: &[String],
|
||||
) -> Result<HashSet<EventId>, String> {
|
||||
// Step 1: Query all potentially affected events (same as simple cascade)
|
||||
let potentially_affected = query_dependent_events(database, event_ids, addresses).await?;
|
||||
|
||||
tracing::debug!(
|
||||
event_ids_count = event_ids.len(),
|
||||
addresses_count = addresses.len(),
|
||||
potentially_affected_count = potentially_affected.len(),
|
||||
"Queried potentially affected events for graph-based deletion"
|
||||
);
|
||||
|
||||
// Step 2: Query all events to build the graph
|
||||
// We need ALL events in the database to properly build the dependency graph
|
||||
let all_events = query_all_events(database).await?;
|
||||
|
||||
tracing::debug!(
|
||||
all_events_count = all_events.len(),
|
||||
"Queried all events for graph building"
|
||||
);
|
||||
|
||||
// Step 3: Build event dependency graph
|
||||
let graph = super::build_event_graph(&all_events);
|
||||
|
||||
tracing::info!(
|
||||
nodes = graph.node_count(),
|
||||
edges = graph.edge_count(),
|
||||
max_depth = graph.max_depth(),
|
||||
"Built event dependency graph"
|
||||
);
|
||||
|
||||
// Step 4: Re-evaluate events to determine retention reasons
|
||||
// For each deleted address, run re-evaluation
|
||||
let mut all_kept_events = std::collections::HashMap::new();
|
||||
let mut all_deleted_events = HashSet::new();
|
||||
|
||||
for address in addresses {
|
||||
let reeval_result =
|
||||
super::reevaluate_events_without_announcement(database, address, &potentially_affected)
|
||||
.await?;
|
||||
|
||||
tracing::info!(
|
||||
address = %address,
|
||||
kept = reeval_result.keep.len(),
|
||||
deleted = reeval_result.delete.len(),
|
||||
"Re-evaluation complete for address"
|
||||
);
|
||||
|
||||
// Merge results
|
||||
all_kept_events.extend(reeval_result.keep);
|
||||
all_deleted_events.extend(reeval_result.delete);
|
||||
}
|
||||
|
||||
// Step 5: Traverse graph to mark events for deletion
|
||||
// Use the first deleted event ID for logging (or generate a dummy one)
|
||||
let deleted_event_id = event_ids.first().copied().unwrap_or_else(|| {
|
||||
EventId::from_hex("0000000000000000000000000000000000000000000000000000000000000000")
|
||||
.unwrap()
|
||||
});
|
||||
|
||||
let traversal_result =
|
||||
super::traverse_and_mark_deletions(&graph, &all_kept_events, deleted_event_id);
|
||||
|
||||
// Log retention reasons
|
||||
for (event_id, reason) in &all_kept_events {
|
||||
tracing::debug!(
|
||||
event_id = %event_id,
|
||||
event_type = %reason.event_type,
|
||||
valid_announcements = ?reason.valid_announcements,
|
||||
"Event KEPT: has alternative retention reason"
|
||||
);
|
||||
}
|
||||
|
||||
// Log circular dependencies
|
||||
for circular_dep in &traversal_result.circular_dependencies {
|
||||
if circular_dep.anchored {
|
||||
tracing::info!(
|
||||
events = ?circular_dep.events,
|
||||
"Detected anchored circular dependency (events will be kept)"
|
||||
);
|
||||
} else {
|
||||
tracing::warn!(
|
||||
events = ?circular_dep.events,
|
||||
"Detected unanchored circular dependency (events will be deleted)"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Log summary
|
||||
tracing::info!(
|
||||
total_evaluated = potentially_affected.len(),
|
||||
kept = traversal_result.keep.len(),
|
||||
deleted = traversal_result.delete.len(),
|
||||
circular_deps = traversal_result.circular_dependencies.len(),
|
||||
"Graph-based deletion complete: {} events kept, {} events deleted",
|
||||
traversal_result.keep.len(),
|
||||
traversal_result.delete.len()
|
||||
);
|
||||
|
||||
// Return only events that should be deleted
|
||||
Ok(traversal_result.delete)
|
||||
}
|
||||
|
||||
/// Query all events that depend on the given event IDs
|
||||
///
|
||||
/// This performs a recursive traversal to find:
|
||||
@@ -18,8 +142,7 @@ use crate::git::archive::{archive_repository, create_archive_metadata};
|
||||
/// 2. Events with `e` tags referencing the events found in step 1
|
||||
/// 3. Recursively find events referencing those events
|
||||
///
|
||||
/// For Phase 1, this is a simple cascade (no graph complexity).
|
||||
/// Phase 3 will add multi-maintainer graph algorithm.
|
||||
/// This is used by the graph-based deletion algorithm to find potentially affected events.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `database` - Main database to query
|
||||
@@ -69,6 +192,48 @@ pub async fn query_dependent_events(
|
||||
Ok(all_dependents)
|
||||
}
|
||||
|
||||
/// Query all events from the database
|
||||
///
|
||||
/// This is used by the graph-based deletion algorithm to build the complete
|
||||
/// dependency graph.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `database` - Database to query
|
||||
///
|
||||
/// # Returns
|
||||
/// Vector of all events in the database
|
||||
async fn query_all_events(database: &SharedDatabase) -> Result<Vec<Event>, String> {
|
||||
// Query all NIP-34 event types
|
||||
let kinds = vec![
|
||||
Kind::from(30617), // Repository announcements
|
||||
Kind::from(1617), // Patches
|
||||
Kind::from(1621), // Issues
|
||||
Kind::from(1619), // PR Updates
|
||||
Kind::from(1630), // Issue status
|
||||
Kind::from(1631), // PR status
|
||||
Kind::from(1632), // Patch status
|
||||
Kind::from(1633), // Repository status
|
||||
Kind::from(30618), // Repository state
|
||||
Kind::from(1630), // Pull requests
|
||||
];
|
||||
|
||||
let mut all_events = Vec::new();
|
||||
|
||||
for kind in kinds {
|
||||
let filter = Filter::new().kind(kind);
|
||||
let events: Vec<Event> = database
|
||||
.query(filter)
|
||||
.await
|
||||
.map_err(|e| format!("Database query failed: {}", e))?
|
||||
.into_iter()
|
||||
.collect();
|
||||
|
||||
all_events.extend(events);
|
||||
}
|
||||
|
||||
Ok(all_events)
|
||||
}
|
||||
|
||||
/// Query events that have an `a` tag referencing the given address
|
||||
///
|
||||
/// # Arguments
|
||||
|
||||
@@ -23,7 +23,8 @@ mod traversal;
|
||||
pub use announcement::{AnnouncementPolicy, AnnouncementResult};
|
||||
pub use deletion::{DeletionPolicy, DeletionResult};
|
||||
pub use deletion_ops::{
|
||||
archive_repositories_for_addresses, move_to_holding_database, query_dependent_events,
|
||||
archive_repositories_for_addresses, determine_events_to_delete, move_to_holding_database,
|
||||
query_dependent_events,
|
||||
};
|
||||
pub use graph::{build_event_graph, EventGraph, EventNode};
|
||||
pub use pr_event::PrEventPolicy;
|
||||
|
||||
Reference in New Issue
Block a user