diff options
| author | Bitflash <btf01@proton.me> | 2026-07-31 09:30:41 -0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-07-31 09:30:41 -0300 |
| commit | f2ea483df4b2277abe13cc19bcc0bc5834c2e3e6 (patch) | |
| tree | 6a96f30576fcd6fa18246f3a2ee7492f4d4d4cd0 | |
| parent | 7243bbead29d68c9f8ae9a022669b02179e352e2 (diff) | |
nostr: back off from a relay that is down, and replace one that is (#64)
relay.nostr.band went down and stayed down -- it resolves and refuses TCP
on 443, from several networks. The seed accumulated 1092 identical error
lines about it.
The log noise was the small half. The discovery loop walks every relay in
turn with a ten-second connect timeout, so a dead relay added ten seconds
to every pass, forever, and that loop is what finds peers -- a relay that
answers nobody was slowing down the ones that answer everybody.
Back off per relay: 30s after the first failure, doubling to a fifteen
minute ceiling, cleared the moment it answers again. Only when an attempt
happens changes; nothing about whether discovery is correct.
Also swapped relay.nostr.band for nostr.bitcoiner.social, on 206.245.132.32
-- neither Cloudflare nor Hetzner, which is the diversity rule the list
already follows.
Verified with a deliberately bogus relay in the list: backoff stepped
30s -> 60s -> 120s and the dead entry produced nine lines in four minutes
instead of one per pass, while the node still connected to five relays,
discovered 54 fresh peers and followed the chain.
| -rw-r--r-- | src/nostr.cpp | 81 |
1 files changed, 79 insertions, 2 deletions
diff --git a/src/nostr.cpp b/src/nostr.cpp index 088174a..17139ad 100644 --- a/src/nostr.cpp +++ b/src/nostr.cpp | |||
| @@ -83,7 +83,11 @@ static CCriticalSection cs_btfPeers; | |||
| 83 | const char* pszNostrRelays[] = { | 83 | const char* pszNostrRelays[] = { |
| 84 | "wss://relay.damus.io", | 84 | "wss://relay.damus.io", |
| 85 | "wss://nos.lol", | 85 | "wss://nos.lol", |
| 86 | "wss://relay.nostr.band", | 86 | // Was relay.nostr.band, which went down and stayed down (2026-07-31: |
| 87 | // resolves, refuses TCP on 443, from several networks). Replaced with a | ||
| 88 | // relay on a different upstream again -- 206.245.132.32, neither | ||
| 89 | // Cloudflare nor Hetzner, which the rest of this list already leans on. | ||
| 90 | "wss://nostr.bitcoiner.social", | ||
| 87 | "wss://nostr21.com", | 91 | "wss://nostr21.com", |
| 88 | "wss://offchain.pub", | 92 | "wss://offchain.pub", |
| 89 | "wss://nostr.mom", | 93 | "wss://nostr.mom", |
| @@ -1172,6 +1176,74 @@ bool BtfResolveMany(const std::vector<std::string>& btfAddrs, | |||
| 1172 | } | 1176 | } |
| 1173 | 1177 | ||
| 1174 | 1178 | ||
| 1179 | // ---- per-relay backoff ----------------------------------------------------- | ||
| 1180 | // | ||
| 1181 | // A relay that is down costs the whole discovery loop the full connect timeout, | ||
| 1182 | // every round, forever. relay.nostr.band went down and produced 1092 identical | ||
| 1183 | // error lines on the seed while adding ten seconds to every pass -- and the | ||
| 1184 | // loop is what discovers peers, so a dead relay was slowing down the live ones. | ||
| 1185 | // | ||
| 1186 | // Back off per relay instead: 30s after the first failure, doubling to a | ||
| 1187 | // fifteen-minute ceiling, cleared the moment it answers again. This changes | ||
| 1188 | // only when an attempt happens, never whether discovery is correct. | ||
| 1189 | struct CRelayBackoff | ||
| 1190 | { | ||
| 1191 | int64 nNextTry; | ||
| 1192 | int nFails; | ||
| 1193 | CRelayBackoff() : nNextTry(0), nFails(0) {} | ||
| 1194 | }; | ||
| 1195 | static map<string, CRelayBackoff> mapRelayBackoff; | ||
| 1196 | static CCriticalSection cs_relayBackoff; | ||
| 1197 | |||
| 1198 | static const int64 RELAY_BACKOFF_BASE_SECS = 30; | ||
| 1199 | static const int64 RELAY_BACKOFF_MAX_SECS = 15 * 60; | ||
| 1200 | |||
| 1201 | static bool RelayDueForRetry(const string& relay) | ||
| 1202 | { | ||
| 1203 | CRITICAL_BLOCK(cs_relayBackoff) | ||
| 1204 | { | ||
| 1205 | map<string, CRelayBackoff>::iterator it = mapRelayBackoff.find(relay); | ||
| 1206 | if (it == mapRelayBackoff.end()) | ||
| 1207 | return true; | ||
| 1208 | return GetTime() >= it->second.nNextTry; | ||
| 1209 | } | ||
| 1210 | return true; | ||
| 1211 | } | ||
| 1212 | |||
| 1213 | static void RelayNoteFailure(const string& relay) | ||
| 1214 | { | ||
| 1215 | CRITICAL_BLOCK(cs_relayBackoff) | ||
| 1216 | { | ||
| 1217 | CRelayBackoff& b = mapRelayBackoff[relay]; | ||
| 1218 | b.nFails++; | ||
| 1219 | int64 nWait = RELAY_BACKOFF_BASE_SECS; | ||
| 1220 | for (int i = 1; i < b.nFails && nWait < RELAY_BACKOFF_MAX_SECS; i++) | ||
| 1221 | nWait *= 2; | ||
| 1222 | if (nWait > RELAY_BACKOFF_MAX_SECS) | ||
| 1223 | nWait = RELAY_BACKOFF_MAX_SECS; | ||
| 1224 | b.nNextTry = GetTime() + nWait; | ||
| 1225 | // Only on the way in, so a relay that stays down says this once per | ||
| 1226 | // backoff step rather than once per pass. | ||
| 1227 | LogPrint("nostr", "Nostr: %s unreachable (%d in a row), not retrying for %ds\n", | ||
| 1228 | relay.c_str(), b.nFails, (int)nWait); | ||
| 1229 | } | ||
| 1230 | } | ||
| 1231 | |||
| 1232 | static void RelayNoteSuccess(const string& relay) | ||
| 1233 | { | ||
| 1234 | CRITICAL_BLOCK(cs_relayBackoff) | ||
| 1235 | { | ||
| 1236 | map<string, CRelayBackoff>::iterator it = mapRelayBackoff.find(relay); | ||
| 1237 | if (it != mapRelayBackoff.end()) | ||
| 1238 | { | ||
| 1239 | if (it->second.nFails > 0) | ||
| 1240 | LogPrint("nostr", "Nostr: %s answering again after %d failure(s)\n", | ||
| 1241 | relay.c_str(), it->second.nFails); | ||
| 1242 | mapRelayBackoff.erase(it); | ||
| 1243 | } | ||
| 1244 | } | ||
| 1245 | } | ||
| 1246 | |||
| 1175 | // Connect to a relay: publish our .btf descriptor and collect relay data. | 1247 | // Connect to a relay: publish our .btf descriptor and collect relay data. |
| 1176 | static bool SeedFromRelay(CNostrKey& key, const string& relay) | 1248 | static bool SeedFromRelay(CNostrKey& key, const string& relay) |
| 1177 | { | 1249 | { |
| @@ -1653,9 +1725,14 @@ void ThreadNostrSeed(void* parg) | |||
| 1653 | for (int i = 0; i < nNostrRelays; i++) | 1725 | for (int i = 0; i < nNostrRelays; i++) |
| 1654 | { | 1726 | { |
| 1655 | if (fShutdown) return; | 1727 | if (fShutdown) return; |
| 1728 | if (!RelayDueForRetry(pszNostrRelays[i])) | ||
| 1729 | continue; | ||
| 1656 | try | 1730 | try |
| 1657 | { | 1731 | { |
| 1658 | SeedFromRelay(key, pszNostrRelays[i]); | 1732 | if (SeedFromRelay(key, pszNostrRelays[i])) |
| 1733 | RelayNoteSuccess(pszNostrRelays[i]); | ||
| 1734 | else | ||
| 1735 | RelayNoteFailure(pszNostrRelays[i]); | ||
| 1659 | } | 1736 | } |
| 1660 | CATCH_PRINT_EXCEPTION("SeedFromRelay") | 1737 | CATCH_PRINT_EXCEPTION("SeedFromRelay") |
| 1661 | } | 1738 | } |