add logging for tracking advert blocking

This commit is contained in:
Lloyd
2026-03-05 11:26:40 +00:00
parent 7eeba4d569
commit 2b7d394938
21 changed files with 116 additions and 22 deletions

View File

@@ -113,6 +113,16 @@ class AdvertHelper:
self._stats_adverts_allowed = 0
self._stats_adverts_dropped = 0
self._stats_tier_changes = 0
# Recent drops tracking (keep last 20)
self._recent_drops = []
self._max_recent_drops = 20
# Memory management
self._last_cleanup = time.time()
self._cleanup_interval_seconds = 3600.0 # Clean up every hour
self._bucket_state_retention_seconds = 604800.0 # Keep inactive pubkeys for 7 days
self._max_tracked_pubkeys = 10000 # Hard limit on tracked pubkeys
logger.info(
f"Advert limiter: adaptive={self._adaptive_enabled}, "
@@ -121,6 +131,64 @@ class AdvertHelper:
f"penalty={self._penalty_enabled}"
)
# -------------------------------------------------------------------------
# Memory management
# -------------------------------------------------------------------------
def _cleanup_old_state(self, now: float) -> None:
"""Clean up old/expired entries to prevent unbounded memory growth."""
# 1. Remove expired penalties
expired_penalties = [pk for pk, until in self._penalty_until.items() if until < now]
for pk in expired_penalties:
del self._penalty_until[pk]
# 2. Remove old bucket states for inactive pubkeys
inactive_pubkeys = [
pk for pk, state in self._bucket_state.items()
if now - state.get("last_seen", 0) > self._bucket_state_retention_seconds
]
for pk in inactive_pubkeys:
del self._bucket_state[pk]
# Also clean up related violation state
if pk in self._violation_state:
del self._violation_state[pk]
# 3. Decay old violations based on decay time
for pk, vstate in list(self._violation_state.items()):
last_violation = vstate.get("last_violation", 0)
if now - last_violation > self._penalty_decay_seconds:
# Reset violation count after decay period
vstate["count"] = 0
# 4. Hard limit: if we're tracking too many pubkeys, remove oldest inactive ones
if len(self._bucket_state) > self._max_tracked_pubkeys:
# Sort by last_seen and remove oldest 10%
sorted_pubkeys = sorted(
self._bucket_state.items(),
key=lambda x: x[1].get("last_seen", 0)
)
to_remove = int(len(sorted_pubkeys) * 0.1)
for pk, _ in sorted_pubkeys[:to_remove]:
del self._bucket_state[pk]
if pk in self._violation_state:
del self._violation_state[pk]
if pk in self._penalty_until:
del self._penalty_until[pk]
# 5. Limit known neighbors set to prevent unbounded growth
if len(self._known_neighbors) > 1000:
# Clear the oldest half (simple approach - could be more sophisticated)
self._known_neighbors = set(list(self._known_neighbors)[500:])
if expired_penalties or inactive_pubkeys:
logger.debug(
f"Cleaned up {len(expired_penalties)} expired penalties, "
f"{len(inactive_pubkeys)} inactive pubkeys. "
f"Tracking: {len(self._bucket_state)} buckets, "
f"{len(self._penalty_until)} penalties, "
f"{len(self._known_neighbors)} neighbors"
)
# -------------------------------------------------------------------------
# Adaptive tier calculation
# -------------------------------------------------------------------------
@@ -148,6 +216,11 @@ class AdvertHelper:
self._packets_in_window = 0
self._duplicates_in_window = 0
self._last_metrics_update = now
# Periodic cleanup
if now - self._last_cleanup >= self._cleanup_interval_seconds:
self._cleanup_old_state(now)
self._last_cleanup = now
# Count this event
if is_advert:
@@ -385,6 +458,15 @@ class AdvertHelper:
"active_penalties": active_penalties,
"tracked_pubkeys": len(self._bucket_state),
"bucket_states": bucket_summary,
"recent_drops": [
{
"pubkey": drop["pubkey"],
"name": drop["name"],
"reason": drop["reason"],
"seconds_ago": round(now - drop["timestamp"], 1)
}
for drop in reversed(self._recent_drops) # Most recent first
],
}
async def process_advert_packet(self, packet, rssi: int, snr: float) -> None:
@@ -422,9 +504,21 @@ class AdvertHelper:
now = time.time()
allowed, reason = self._allow_advert(pubkey, now)
if not allowed:
logger.warning(f"Dropping advert {pubkey[:16]}...: {reason}")
logger.warning(f"Dropping advert from '{node_name}' ({pubkey[:16]}...): {reason}")
packet.mark_do_not_retransmit()
packet.drop_reason = reason
# Track recent drop
self._recent_drops.append({
"pubkey": pubkey[:16],
"name": node_name,
"reason": reason,
"timestamp": now
})
# Keep only last N drops
if len(self._recent_drops) > self._max_recent_drops:
self._recent_drops.pop(0)
return
# Skip our own adverts

View File

@@ -1 +1 @@
import{a as p,b as n,g as m,e as t,s as g,t as s,j as d,p as l}from"./index-BxyNawLf.js";const f={class:"flex items-center justify-between mb-4"},w={class:"text-xl font-semibold text-content-primary dark:text-content-primary"},v={class:"mb-6"},h={key:0,class:"w-6 h-6",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},y={key:1,class:"w-6 h-6",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},C={key:2,class:"w-6 h-6",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},B={class:"text-content-secondary dark:text-content-primary/80 text-base leading-relaxed"},j={class:"flex gap-3"},_=p({__name:"ConfirmDialog",props:{show:{type:Boolean},title:{default:"Confirm Action"},message:{},confirmText:{default:"Confirm"},cancelText:{default:"Cancel"},variant:{default:"warning"}},emits:["close","confirm"],setup(c,{emit:b}){const o=c,r=b,u=i=>{i.target===i.currentTarget&&r("close")},k={danger:"bg-red-100 dark:bg-red-500/20 border-red-500/30 text-red-600 dark:text-red-400",warning:"bg-yellow-100 dark:bg-yellow-500/20 border-yellow-500/30 text-yellow-600 dark:text-yellow-400",info:"bg-blue-500/20 border-blue-500/30 text-blue-600 dark:text-blue-400"},x={danger:"bg-red-500 hover:bg-red-600",warning:"bg-yellow-500 hover:bg-yellow-600",info:"bg-blue-500 hover:bg-blue-600"};return(i,e)=>o.show?(l(),n("div",{key:0,onClick:u,class:"fixed inset-0 bg-black/40 backdrop-blur-lg z-[99999] flex items-center justify-center p-4",style:{"backdrop-filter":"blur(8px) saturate(180%)",position:"fixed",top:"0",left:"0",right:"0",bottom:"0"}},[t("div",{class:"bg-white dark:bg-surface-elevated backdrop-blur-xl rounded-[20px] p-6 w-full max-w-md border border-stroke-subtle dark:border-white/10",onClick:e[3]||(e[3]=g(()=>{},["stop"]))},[t("div",f,[t("h3",w,s(o.title),1),t("button",{onClick:e[0]||(e[0]=a=>r("close")),class:"text-content-secondary dark:text-content-muted hover:text-content-primary dark:hover:text-content-primary transition-colors"},e[4]||(e[4]=[t("svg",{class:"w-6 h-6",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},[t("path",{"stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"2",d:"M6 18L18 6M6 6l12 12"})],-1)]))]),t("div",v,[t("div",{class:d(["inline-flex p-3 rounded-xl mb-4",k[o.variant]])},[o.variant==="danger"?(l(),n("svg",h,e[5]||(e[5]=[t("path",{"stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"2",d:"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"},null,-1)]))):o.variant==="warning"?(l(),n("svg",y,e[6]||(e[6]=[t("path",{"stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"2",d:"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"},null,-1)]))):(l(),n("svg",C,e[7]||(e[7]=[t("path",{"stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"2",d:"M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"},null,-1)])))],2),t("p",B,s(o.message),1)]),t("div",j,[t("button",{onClick:e[1]||(e[1]=a=>r("close")),class:"flex-1 px-4 py-3 rounded-xl bg-background-mute dark:bg-white/5 hover:bg-stroke-subtle dark:hover:bg-white/10 text-content-primary dark:text-content-primary transition-all duration-200 border border-stroke-subtle dark:border-stroke/10"},s(o.cancelText),1),t("button",{onClick:e[2]||(e[2]=a=>r("confirm")),class:d(["flex-1 px-4 py-3 rounded-xl text-white transition-all duration-200",x[o.variant]])},s(o.confirmText),3)])])])):m("",!0)}});export{_};
import{a as p,b as n,g as m,e as t,s as g,t as s,j as d,p as l}from"./index-DeBe-keE.js";const f={class:"flex items-center justify-between mb-4"},w={class:"text-xl font-semibold text-content-primary dark:text-content-primary"},v={class:"mb-6"},h={key:0,class:"w-6 h-6",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},y={key:1,class:"w-6 h-6",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},C={key:2,class:"w-6 h-6",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},B={class:"text-content-secondary dark:text-content-primary/80 text-base leading-relaxed"},j={class:"flex gap-3"},_=p({__name:"ConfirmDialog",props:{show:{type:Boolean},title:{default:"Confirm Action"},message:{},confirmText:{default:"Confirm"},cancelText:{default:"Cancel"},variant:{default:"warning"}},emits:["close","confirm"],setup(c,{emit:b}){const o=c,r=b,u=i=>{i.target===i.currentTarget&&r("close")},k={danger:"bg-red-100 dark:bg-red-500/20 border-red-500/30 text-red-600 dark:text-red-400",warning:"bg-yellow-100 dark:bg-yellow-500/20 border-yellow-500/30 text-yellow-600 dark:text-yellow-400",info:"bg-blue-500/20 border-blue-500/30 text-blue-600 dark:text-blue-400"},x={danger:"bg-red-500 hover:bg-red-600",warning:"bg-yellow-500 hover:bg-yellow-600",info:"bg-blue-500 hover:bg-blue-600"};return(i,e)=>o.show?(l(),n("div",{key:0,onClick:u,class:"fixed inset-0 bg-black/40 backdrop-blur-lg z-[99999] flex items-center justify-center p-4",style:{"backdrop-filter":"blur(8px) saturate(180%)",position:"fixed",top:"0",left:"0",right:"0",bottom:"0"}},[t("div",{class:"bg-white dark:bg-surface-elevated backdrop-blur-xl rounded-[20px] p-6 w-full max-w-md border border-stroke-subtle dark:border-white/10",onClick:e[3]||(e[3]=g(()=>{},["stop"]))},[t("div",f,[t("h3",w,s(o.title),1),t("button",{onClick:e[0]||(e[0]=a=>r("close")),class:"text-content-secondary dark:text-content-muted hover:text-content-primary dark:hover:text-content-primary transition-colors"},e[4]||(e[4]=[t("svg",{class:"w-6 h-6",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},[t("path",{"stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"2",d:"M6 18L18 6M6 6l12 12"})],-1)]))]),t("div",v,[t("div",{class:d(["inline-flex p-3 rounded-xl mb-4",k[o.variant]])},[o.variant==="danger"?(l(),n("svg",h,e[5]||(e[5]=[t("path",{"stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"2",d:"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"},null,-1)]))):o.variant==="warning"?(l(),n("svg",y,e[6]||(e[6]=[t("path",{"stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"2",d:"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"},null,-1)]))):(l(),n("svg",C,e[7]||(e[7]=[t("path",{"stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"2",d:"M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"},null,-1)])))],2),t("p",B,s(o.message),1)]),t("div",j,[t("button",{onClick:e[1]||(e[1]=a=>r("close")),class:"flex-1 px-4 py-3 rounded-xl bg-background-mute dark:bg-white/5 hover:bg-stroke-subtle dark:hover:bg-white/10 text-content-primary dark:text-content-primary transition-all duration-200 border border-stroke-subtle dark:border-stroke/10"},s(o.cancelText),1),t("button",{onClick:e[2]||(e[2]=a=>r("confirm")),class:d(["flex-1 px-4 py-3 rounded-xl text-white transition-all duration-200",x[o.variant]])},s(o.confirmText),3)])])])):m("",!0)}});export{_};

View File

@@ -1 +1 @@
import{a as e,b as r,i as o,p as n}from"./index-BxyNawLf.js";const d=e({name:"HelpView",__name:"Help",setup(a){return(i,t)=>(n(),r("div",null,t[0]||(t[0]=[o('<div class="glass-card backdrop-blur border border-stroke-subtle dark:border-white/10 rounded-[15px] p-8"><h1 class="text-content-primary dark:text-content-primary text-2xl font-semibold mb-6">Help &amp; Documentation</h1><div class="text-center py-12"><div class="text-primary mb-6"><svg class="w-20 h-20 mx-auto mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.746 0 3.332.477 4.5 1.253v13C19.832 18.477 18.246 18 16.5 18c-1.746 0-3.332.477-4.5 1.253"></path></svg></div><h2 class="text-content-primary dark:text-content-primary text-xl font-medium mb-3">pyMC Repeater Wiki</h2><p class="text-content-secondary dark:text-content-muted mb-8 max-w-md mx-auto"> Access documentation, setup guides, troubleshooting tips, and community resources on our official wiki. </p><a href="https://github.com/rightup/pyMC_Repeater/wiki" target="_blank" rel="noopener noreferrer" class="inline-flex items-center gap-2 bg-primary hover:bg-primary/80 text-white dark:text-background font-medium py-3 px-6 rounded-xl transition-colors duration-200"><svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"></path></svg> Visit Wiki Documentation </a><div class="mt-8 text-xs text-content-muted dark:text-content-muted"> Opens in a new tab </div></div></div>',1)])))}});export{d as default};
import{a as e,b as r,i as o,p as n}from"./index-DeBe-keE.js";const d=e({name:"HelpView",__name:"Help",setup(a){return(i,t)=>(n(),r("div",null,t[0]||(t[0]=[o('<div class="glass-card backdrop-blur border border-stroke-subtle dark:border-white/10 rounded-[15px] p-8"><h1 class="text-content-primary dark:text-content-primary text-2xl font-semibold mb-6">Help &amp; Documentation</h1><div class="text-center py-12"><div class="text-primary mb-6"><svg class="w-20 h-20 mx-auto mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.746 0 3.332.477 4.5 1.253v13C19.832 18.477 18.246 18 16.5 18c-1.746 0-3.332.477-4.5 1.253"></path></svg></div><h2 class="text-content-primary dark:text-content-primary text-xl font-medium mb-3">pyMC Repeater Wiki</h2><p class="text-content-secondary dark:text-content-muted mb-8 max-w-md mx-auto"> Access documentation, setup guides, troubleshooting tips, and community resources on our official wiki. </p><a href="https://github.com/rightup/pyMC_Repeater/wiki" target="_blank" rel="noopener noreferrer" class="inline-flex items-center gap-2 bg-primary hover:bg-primary/80 text-white dark:text-background font-medium py-3 px-6 rounded-xl transition-colors duration-200"><svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"></path></svg> Visit Wiki Documentation </a><div class="mt-8 text-xs text-content-muted dark:text-content-muted"> Opens in a new tab </div></div></div>',1)])))}});export{d as default};

File diff suppressed because one or more lines are too long

View File

@@ -1,4 +1,4 @@
import{L as J,a as zl,r as ut,o as Hl,$ as Ul,P as Rn,D as ql,b as tt,e as Z,g as Yt,t as Is,w as Kl,v as Vl,X as Ji,j as Tn,s as jl,p as it,x as Gl}from"./index-BxyNawLf.js";/**
import{L as J,a as zl,r as ut,o as Hl,$ as Ul,P as Rn,D as ql,b as tt,e as Z,g as Yt,t as Is,w as Kl,v as Vl,X as Ji,j as Tn,s as jl,p as it,x as Gl}from"./index-DeBe-keE.js";/**
* Copyright (c) 2014-2024 The xterm.js authors. All rights reserved.
* @license MIT
*

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
import{M as x,c as s}from"./index-BxyNawLf.js";const l={7:-7.5,8:-10,9:-12.5,10:-15,11:-17.5,12:-20},d=-116,i=8,u=5;function y(t,e){return t-e}function S(t){return l[t]??l[i]}function f(t,e){const r=e+u;if(t<=e){const o=t<=e-5?0:1;return{bars:o,color:"text-red-600 dark:text-red-400",snr:t,quality:o===0?"none":"poor"}}if(t<r){const n=(t-e)/u<.5?2:3;return{bars:n,color:n===2?"text-orange-600 dark:text-orange-400":"text-yellow-600 dark:text-yellow-400",snr:t,quality:"fair"}}const a=t-r>=10?5:4;return{bars:a,color:a===5?"text-green-600 dark:text-green-400":"text-green-600 dark:text-green-300",snr:t,quality:a===5?"excellent":"good"}}function N(){const t=x(),e=s(()=>t.noiseFloorDbm??d),r=s(()=>t.stats?.config?.radio?.spreading_factor??i),c=s(()=>S(r.value));return{getSignalQuality:o=>{if(!o||o>0||o<-120)return{bars:0,color:"text-gray-400 dark:text-gray-500",snr:-999,quality:"none"};const n=y(o,e.value),g=Math.max(-30,Math.min(20,n));return f(g,c.value)},noiseFloor:e,spreadingFactor:r,minSNR:c}}export{N as u};
import{M as x,c as s}from"./index-DeBe-keE.js";const l={7:-7.5,8:-10,9:-12.5,10:-15,11:-17.5,12:-20},d=-116,i=8,u=5;function y(t,e){return t-e}function S(t){return l[t]??l[i]}function f(t,e){const r=e+u;if(t<=e){const o=t<=e-5?0:1;return{bars:o,color:"text-red-600 dark:text-red-400",snr:t,quality:o===0?"none":"poor"}}if(t<r){const n=(t-e)/u<.5?2:3;return{bars:n,color:n===2?"text-orange-600 dark:text-orange-400":"text-yellow-600 dark:text-yellow-400",snr:t,quality:"fair"}}const a=t-r>=10?5:4;return{bars:a,color:a===5?"text-green-600 dark:text-green-400":"text-green-600 dark:text-green-300",snr:t,quality:a===5?"excellent":"good"}}function N(){const t=x(),e=s(()=>t.noiseFloorDbm??d),r=s(()=>t.stats?.config?.radio?.spreading_factor??i),c=s(()=>S(r.value));return{getSignalQuality:o=>{if(!o||o>0||o<-120)return{bars:0,color:"text-gray-400 dark:text-gray-500",snr:-999,quality:"none"};const n=y(o,e.value),g=Math.max(-30,Math.min(20,n));return f(g,c.value)},noiseFloor:e,spreadingFactor:r,minSNR:c}}export{N as u};

View File

@@ -8,8 +8,8 @@
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans:wght@400;500;600;700&display=swap" rel="stylesheet">
<script type="module" crossorigin src="/assets/index-BxyNawLf.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-O_jlX9bz.css">
<script type="module" crossorigin src="/assets/index-DeBe-keE.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-ZoIbdUUa.css">
</head>
<body>
<div id="app"></div>