feat(config): make holding cleanup timings configurable

This commit is contained in:
DanConwayDev
2026-06-18 11:47:09 +00:00
parent 0a0f7c3570
commit 80c17c7f61
5 changed files with 135 additions and 12 deletions
+18 -1
View File
@@ -169,6 +169,23 @@
# Default: 12
# NGIT_NAUGHTY_LIST_EXPIRATION_HOURS=12
# ============================================================================
# HOLDING DB CLEANUP
# ============================================================================
# Retention window in seconds for deleted events kept in holding DB
# Used by startup catch-up and periodic expiration cleanup
# Must be greater than 0
# CLI: --holding-retention-secs <seconds>
# Default: 7776000 (90 days)
# NGIT_HOLDING_RETENTION_SECS=7776000
# Interval in seconds between holding DB background cleanup passes
# Must be greater than 0
# CLI: --holding-cleanup-interval-secs <seconds>
# Default: 86400 (24 hours)
# NGIT_HOLDING_CLEANUP_INTERVAL_SECS=86400
# ============================================================================
# GRASP-05 ARCHIVE MODE
# ============================================================================
@@ -345,4 +362,4 @@
# When unset (default), connections are unlimited (defers to OS fd limits and infrastructure controls)
# CLI: --max-connections <count>
# Default: unlimited
# NGIT_MAX_CONNECTIONS=4096
# NGIT_MAX_CONNECTIONS=4096
+52
View File
@@ -492,6 +492,58 @@ NGIT_REJECTED_COLD_INDEX_EXPIRY_SECS=1209600
---
### Holding DB Cleanup Configuration
These options control retention and cleanup cadence for deleted events archived in the holding database.
#### `NGIT_HOLDING_RETENTION_SECS`
**Description:** How long deleted events are retained in the holding DB before expiration cleanup removes them
**Type:** Integer (seconds)
**Default:** `7776000` (90 days)
**Required:** No
**Examples:**
```bash
# Default: 90 days
NGIT_HOLDING_RETENTION_SECS=7776000
# Short retention (30 days)
NGIT_HOLDING_RETENTION_SECS=2592000
```
**Notes:**
- Must be greater than 0
- Used by both startup catch-up cleanup and periodic cleanup passes
---
#### `NGIT_HOLDING_CLEANUP_INTERVAL_SECS`
**Description:** Interval between periodic holding DB expiration cleanup passes
**Type:** Integer (seconds)
**Default:** `86400` (24 hours)
**Required:** No
**Examples:**
```bash
# Default: daily cleanup
NGIT_HOLDING_CLEANUP_INTERVAL_SECS=86400
# Every 6 hours
NGIT_HOLDING_CLEANUP_INTERVAL_SECS=21600
```
**Notes:**
- Must be greater than 0
- Smaller values clean up expired records sooner at the cost of more background work
---
### GRASP-05 Archive Configuration
These options enable archive/mirror/backup mode per the GRASP-05 specification.
+17
View File
@@ -178,6 +178,20 @@ let
description = "Hours before removing relay from naughty list";
};
holdingRetentionSecs = mkOption {
type = types.int;
default = 7776000;
description =
"Retention window in seconds for deleted events in holding DB (default: 90 days)";
};
holdingCleanupIntervalSecs = mkOption {
type = types.int;
default = 86400;
description =
"Interval in seconds between holding DB cleanup passes (default: 24 hours)";
};
archiveAll = mkOption {
type = types.bool;
default = false;
@@ -369,6 +383,9 @@ let
toString cfg.rejectedColdIndexExpirySecs;
NGIT_NAUGHTY_LIST_EXPIRATION_HOURS =
toString cfg.naughtyListExpirationHours;
NGIT_HOLDING_RETENTION_SECS = toString cfg.holdingRetentionSecs;
NGIT_HOLDING_CLEANUP_INTERVAL_SECS =
toString cfg.holdingCleanupIntervalSecs;
NGIT_ARCHIVE_ALL = if cfg.archiveAll then "true" else "false";
NGIT_ARCHIVE_WHITELIST = concatStringsSep "," cfg.archiveWhitelist;
NGIT_ARCHIVE_GRASP_SERVICES =
+42
View File
@@ -4,6 +4,7 @@ use nostr_sdk::prelude::*;
use serde::{Deserialize, Serialize};
use std::fs;
use std::path::PathBuf;
use std::time::Duration;
/// Whitelist entry for repository/archive filtering
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
@@ -428,6 +429,22 @@ pub struct Config {
#[arg(long, env = "NGIT_NAUGHTY_LIST_EXPIRATION_HOURS", default_value_t = 12)]
pub naughty_list_expiration_hours: u64,
/// Retention window in seconds for deleted events kept in holding DB.
#[arg(
long,
env = "NGIT_HOLDING_RETENTION_SECS",
default_value_t = crate::nostr::holding::DEFAULT_RETENTION.as_secs()
)]
pub holding_retention_secs: u64,
/// Interval in seconds between background holding cleanup passes.
#[arg(
long,
env = "NGIT_HOLDING_CLEANUP_INTERVAL_SECS",
default_value_t = crate::nostr::holding::DEFAULT_CLEANUP_INTERVAL.as_secs()
)]
pub holding_cleanup_interval_secs: u64,
/// Enable GRASP-05 archive mode: accept all announcements regardless of listing (WARNING: storage risk)
#[arg(long, env = "NGIT_ARCHIVE_ALL", default_value_t = false)]
pub archive_all: bool,
@@ -661,6 +678,18 @@ impl Config {
// Validate repository whitelist configuration
let repository_whitelist = WhitelistEntry::parse_whitelist(&self.repository_whitelist);
if self.holding_retention_secs == 0 {
return Err(anyhow!(
"NGIT_HOLDING_RETENTION_SECS must be greater than 0"
));
}
if self.holding_cleanup_interval_secs == 0 {
return Err(anyhow!(
"NGIT_HOLDING_CLEANUP_INTERVAL_SECS must be greater than 0"
));
}
// Fatal error: repository_whitelist with archive_read_only=true (incompatible)
if !repository_whitelist.is_empty() {
let read_only = self.archive_read_only.unwrap_or(archive_enabled);
@@ -751,6 +780,16 @@ impl Config {
EventBlacklistConfig { blacklisted_npubs }
}
/// Holding DB retention as a duration.
pub fn holding_retention(&self) -> Duration {
Duration::from_secs(self.holding_retention_secs)
}
/// Holding DB periodic cleanup interval as a duration.
pub fn holding_cleanup_interval(&self) -> Duration {
Duration::from_secs(self.holding_cleanup_interval_secs)
}
/// Create config for testing
#[cfg(test)]
pub fn for_testing() -> Self {
@@ -781,6 +820,9 @@ impl Config {
rejected_hot_cache_duration_secs: 120,
rejected_cold_index_expiry_secs: 604800,
naughty_list_expiration_hours: 12,
holding_retention_secs: crate::nostr::holding::DEFAULT_RETENTION.as_secs(),
holding_cleanup_interval_secs: crate::nostr::holding::DEFAULT_CLEANUP_INTERVAL
.as_secs(),
archive_all: false,
archive_whitelist: String::new(),
archive_grasp_services: String::new(),
+6 -11
View File
@@ -399,11 +399,10 @@ async fn run_relay(config: Config) -> Result<()> {
// 1) startup catch-up pass (runs once)
// 2) periodic background cleanup pass
let holding_cleanup_store = relay_with_db.holding.clone();
let holding_retention = config.holding_retention();
let holding_cleanup_interval = config.holding_cleanup_interval();
match holding_cleanup_store
.cleanup_expired(
nostr_sdk::prelude::Timestamp::now(),
nostr::holding::DEFAULT_RETENTION,
)
.cleanup_expired(nostr_sdk::prelude::Timestamp::now(), holding_retention)
.await
{
Ok(stats) => {
@@ -423,7 +422,6 @@ async fn run_relay(config: Config) -> Result<()> {
}
let (holding_cleanup_shutdown_tx, mut holding_cleanup_shutdown_rx) = watch::channel(false);
let holding_cleanup_interval = nostr::holding::DEFAULT_CLEANUP_INTERVAL;
let holding_cleanup_task = tokio::spawn(async move {
let mut interval = tokio::time::interval_at(
tokio::time::Instant::now() + holding_cleanup_interval,
@@ -434,10 +432,7 @@ async fn run_relay(config: Config) -> Result<()> {
tokio::select! {
_ = interval.tick() => {
match holding_cleanup_store
.cleanup_expired(
nostr_sdk::prelude::Timestamp::now(),
nostr::holding::DEFAULT_RETENTION,
)
.cleanup_expired(nostr_sdk::prelude::Timestamp::now(), holding_retention)
.await
{
Ok(stats) => {
@@ -466,8 +461,8 @@ async fn run_relay(config: Config) -> Result<()> {
}
});
info!(
retention_secs = nostr::holding::DEFAULT_RETENTION.as_secs(),
interval_secs = nostr::holding::DEFAULT_CLEANUP_INTERVAL.as_secs(),
retention_secs = holding_retention.as_secs(),
interval_secs = holding_cleanup_interval.as_secs(),
"Holding cleanup task started"
);