1
0
mirror of git://jb55.com/damus synced 2024-09-18 19:23:49 +00:00

Revert "Reduce battery usage by using exp backoff on connections"

This is causing pretty bad fail to reconnect issues

This reverts commit 252a77fd97, reversing
changes made to a611a5d252.
This commit is contained in:
William Casarin 2023-03-17 07:54:29 -06:00
parent e0984aab34
commit 9091cb1aae
12 changed files with 161 additions and 323 deletions

View File

@ -223,7 +223,6 @@
4CF0ABF029857E9200D66079 /* Bech32Object.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF0ABEF29857E9200D66079 /* Bech32Object.swift */; };
4CF0ABF62985CD5500D66079 /* UserSearch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF0ABF52985CD5500D66079 /* UserSearch.swift */; };
4FE60CDD295E1C5E00105A1F /* Wallet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FE60CDC295E1C5E00105A1F /* Wallet.swift */; };
5023E76329AA3627007D3D50 /* RelayPoolTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5023E76229AA3627007D3D50 /* RelayPoolTests.swift */; };
50A50A8D29A09E1C00C01BE7 /* RequestTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A50A8C29A09E1C00C01BE7 /* RequestTests.swift */; };
5C513FBA297F72980072348F /* CustomPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C513FB9297F72980072348F /* CustomPicker.swift */; };
5C513FCC2984ACA60072348F /* QRCodeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C513FCB2984ACA60072348F /* QRCodeView.swift */; };
@ -594,7 +593,6 @@
4CF0ABEF29857E9200D66079 /* Bech32Object.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bech32Object.swift; sourceTree = "<group>"; };
4CF0ABF52985CD5500D66079 /* UserSearch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSearch.swift; sourceTree = "<group>"; };
4FE60CDC295E1C5E00105A1F /* Wallet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Wallet.swift; sourceTree = "<group>"; };
5023E76229AA3627007D3D50 /* RelayPoolTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayPoolTests.swift; sourceTree = "<group>"; };
50A50A8C29A09E1C00C01BE7 /* RequestTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestTests.swift; sourceTree = "<group>"; };
5C513FB9297F72980072348F /* CustomPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomPicker.swift; sourceTree = "<group>"; };
5C513FCB2984ACA60072348F /* QRCodeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCodeView.swift; sourceTree = "<group>"; };
@ -1087,7 +1085,6 @@
4CE6DEF627F7A08200C66700 /* damusTests */ = {
isa = PBXGroup;
children = (
5023E76229AA3627007D3D50 /* RelayPoolTests.swift */,
50A50A8C29A09E1C00C01BE7 /* RequestTests.swift */,
DD597CBC2963D85A00C64D32 /* MarkdownTests.swift */,
4C90BD1B283AC38E008EE7EF /* Bech32Tests.swift */,
@ -1594,7 +1591,6 @@
DD597CBD2963D85A00C64D32 /* MarkdownTests.swift in Sources */,
3A3040EF29A8FEE9008A0F29 /* EventDetailBarTests.swift in Sources */,
4C3EA67B28FF7B3900C48A62 /* InvoiceTests.swift in Sources */,
5023E76329AA3627007D3D50 /* RelayPoolTests.swift in Sources */,
4C363A9E2828A822006E126D /* ReplyTests.swift in Sources */,
4CB883AA297612FF00DC99E7 /* ZapTests.swift in Sources */,
4CB8839A297322D200DC99E7 /* DMTests.swift in Sources */,

View File

@ -31,9 +31,9 @@ class EventsModel: ObservableObject {
}
func subscribe() {
state.pool.subscribe_to(sub_id: sub_id,
filters: [get_filter()],
handler: handle_nostr_event)
state.pool.subscribe(sub_id: sub_id,
filters: [get_filter()],
handler: handle_nostr_event)
}
func unsubscribe() {

View File

@ -40,7 +40,7 @@ class FollowersModel: ObservableObject {
let filter = get_filter()
let filters = [filter]
print_filters(relay_id: "following", filters: [filters])
self.damus_state.pool.subscribe_to(sub_id: sub_id, filters: filters, handler: handle_event)
self.damus_state.pool.subscribe(sub_id: sub_id, filters: filters, handler: handle_event)
}
func unsubscribe() {

View File

@ -41,7 +41,7 @@ class FollowingModel {
}
let filters = [filter]
print_filters(relay_id: "following", filters: [filters])
self.damus_state.pool.subscribe_to(sub_id: sub_id, filters: filters, handler: handle_event)
self.damus_state.pool.subscribe(sub_id: sub_id, filters: filters, handler: handle_event)
}
func unsubscribe() {

View File

@ -83,8 +83,8 @@ class ProfileModel: ObservableObject, Equatable {
print("subscribing to profile \(pubkey) with sub_id \(sub_id)")
print_filters(relay_id: "profile", filters: [[text_filter], [profile_filter]])
damus.pool.subscribe_to(sub_id: sub_id, filters: [text_filter], handler: handle_event)
damus.pool.subscribe_to(sub_id: prof_subid, filters: [profile_filter], handler: handle_event)
damus.pool.subscribe(sub_id: sub_id, filters: [text_filter], handler: handle_event)
damus.pool.subscribe(sub_id: prof_subid, filters: [profile_filter], handler: handle_event)
}
func handle_profile_contact_event(_ ev: NostrEvent) {

View File

@ -38,7 +38,7 @@ class SearchHomeModel: ObservableObject {
func subscribe() {
loading = true
let to_relays = determine_to_relays(pool: damus_state.pool, filters: damus_state.relay_filters)
damus_state.pool.subscribe_to(sub_id: base_subid, filters: [get_base_filter()], to: to_relays, handler: handle_event)
damus_state.pool.subscribe(sub_id: base_subid, filters: [get_base_filter()], handler: handle_event, to: to_relays)
}
func unsubscribe(to: String? = nil) {

View File

@ -104,8 +104,8 @@ class ThreadModel: ObservableObject {
print("subscribing to thread \(event.id) with sub_id \(base_subid)")
loading = true
damus_state.pool.subscribe_to(sub_id: base_subid, filters: base_filters, handler: handle_event)
damus_state.pool.subscribe_to(sub_id: meta_subid, filters: meta_filters, handler: handle_event)
damus_state.pool.subscribe(sub_id: base_subid, filters: base_filters, handler: handle_event)
damus_state.pool.subscribe(sub_id: meta_subid, filters: meta_filters, handler: handle_event)
}
func add_event(_ ev: NostrEvent, privkey: String?) {

View File

@ -29,7 +29,7 @@ class ZapsModel: ObservableObject {
case .note(let note_target):
filter.referenced_ids = [note_target.note_id]
}
state.pool.subscribe_to(sub_id: zaps_subid, filters: [filter], handler: handle_event)
state.pool.subscribe(sub_id: zaps_subid, filters: [filter], handler: handle_event)
}
func unsubscribe() {

View File

@ -14,15 +14,9 @@ enum NostrConnectionEvent {
}
final class RelayConnection: WebSocketDelegate {
enum State {
case notConnected
case connecting
case reconnecting
case connected
case failed
}
private(set) var state: State = .notConnected
private(set) var isConnected = false
private(set) var isConnecting = false
private(set) var isReconnecting = false
private(set) var last_connection_attempt: TimeInterval = 0
private lazy var socket = {
@ -31,36 +25,38 @@ final class RelayConnection: WebSocketDelegate {
socket.delegate = self
return socket
}()
private let eventHandler: (NostrConnectionEvent) -> ()
let url: URL
init(url: URL, eventHandler: @escaping (NostrConnectionEvent) -> ()) {
private var handleEvent: (NostrConnectionEvent) -> ()
private let url: URL
init(url: URL, handleEvent: @escaping (NostrConnectionEvent) -> ()) {
self.url = url
self.eventHandler = eventHandler
self.handleEvent = handleEvent
}
func reconnect() {
if state == .connected {
state = .reconnecting
if isConnected {
isReconnecting = true
disconnect()
} else {
// we're already disconnected, so just connect
connect()
connect(force: true)
}
}
func connect(force: Bool = false) {
if !force && (state == .connected || state == .connecting) {
if !force && (isConnected || isConnecting) {
return
}
state = .connecting
isConnecting = true
last_connection_attempt = Date().timeIntervalSince1970
socket.connect()
}
func disconnect() {
socket.disconnect()
isConnected = false
isConnecting = false
}
func send(_ req: NostrRequest) {
@ -72,52 +68,51 @@ final class RelayConnection: WebSocketDelegate {
socket.write(string: req)
}
private func decodeEvent(_ txt: String) throws -> NostrConnectionEvent {
if let ev = decode_nostr_event(txt: txt) {
return .nostr_event(ev)
} else {
throw DecodingError.dataCorrupted(.init(codingPath: [], debugDescription: "decoding event failed"))
}
}
@MainActor
private func handleEvent(_ event: NostrConnectionEvent) async {
eventHandler(event)
}
// MARK: - WebSocketDelegate
func didReceive(event: WebSocketEvent, client: WebSocket) {
switch event {
case .connected:
state = .connected
self.isConnected = true
self.isConnecting = false
case .disconnected:
if state == .reconnecting {
connect()
} else {
state = .notConnected
self.isConnecting = false
self.isConnected = false
if self.isReconnecting {
self.isReconnecting = false
self.connect()
}
case .cancelled, .error:
state = .failed
self.isConnecting = false
self.isConnected = false
case .text(let txt):
Task(priority: .userInitiated) {
do {
let event = try decodeEvent(txt)
await handleEvent(event)
} catch {
print("decode failed for \(txt): \(error)")
// TODO: trigger event error
if txt.count > 2000 {
DispatchQueue.global(qos: .default).async {
if let ev = decode_nostr_event(txt: txt) {
DispatchQueue.main.async {
self.handleEvent(.nostr_event(ev))
}
return
}
}
} else {
if let ev = decode_nostr_event(txt: txt) {
handleEvent(.nostr_event(ev))
return
}
}
print("decode failed for \(txt)")
// TODO: trigger event error
default:
break
}
eventHandler(.ws_event(event))
handleEvent(.ws_event(event))
}
}

View File

@ -7,6 +7,22 @@
import Foundation
struct SubscriptionId: Identifiable, CustomStringConvertible {
let id: String
var description: String {
id
}
}
struct RelayId: Identifiable, CustomStringConvertible {
let id: String
var description: String {
id
}
}
struct RelayHandler {
let sub_id: String
let callback: (String, NostrConnectionEvent) -> ()
@ -17,58 +33,58 @@ struct QueuedRequest {
let relay: String
}
final class RelayPool {
enum Constants {
/// Used for an exponential backoff algorithm when retrying stale connections
/// Each retry attempt will be delayed by raising this base delay to an exponent
/// equal to the number of previous retries.
static let base_reconnect_delay: TimeInterval = 2
static let max_queued_requests = 10
static let max_retry_attempts = 3
}
private(set) var relays: [Relay] = []
private(set) var handlers: [RelayHandler] = []
private var request_queue: [QueuedRequest] = []
private(set) var seen: Set<String> = Set()
private(set) var counts: [String: UInt64] = [:]
private var retry_attempts_per_relay: [URL: Int] = [:]
struct NostrRequestId: Equatable, Hashable {
let relay: String?
let sub_id: String
}
class RelayPool {
var relays: [Relay] = []
var handlers: [RelayHandler] = []
var request_queue: [QueuedRequest] = []
var seen: Set<String> = Set()
var counts: [String: UInt64] = [:]
var descriptors: [RelayDescriptor] {
relays.map { $0.descriptor }
}
var num_connecting: Int {
relays.reduce(0) { n, r in n + (r.connection.state == .connecting ? 1 : 0) }
return relays.reduce(0) { n, r in n + (r.connection.isConnecting ? 1 : 0) }
}
func remove_handler(sub_id: String) {
guard let index = handlers.firstIndex(where: { $0.sub_id == sub_id }) else {
return
}
handlers.remove(at: index)
self.handlers = handlers.filter { $0.sub_id != sub_id }
print("removing \(sub_id) handler, current: \(handlers.count)")
}
func register_handler(sub_id: String, handler: @escaping (String, NostrConnectionEvent) -> ()) {
guard !handlers.contains(where: { $0.sub_id == sub_id }) else {
return // don't add duplicate handlers
for handler in handlers {
// don't add duplicate handlers
if handler.sub_id == sub_id {
return
}
}
handlers.append(RelayHandler(sub_id: sub_id, callback: handler))
self.handlers.append(RelayHandler(sub_id: sub_id, callback: handler))
print("registering \(sub_id) handler, current: \(self.handlers.count)")
}
func remove_relay(_ relay_id: String) {
disconnect(from: [relay_id])
var i: Int = 0
if let index = relays.firstIndex(where: { $0.id == relay_id }) {
relays.remove(at: index)
self.disconnect(to: [relay_id])
for relay in relays {
if relay.id == relay_id {
relays.remove(at: i)
break
}
i += 1
}
}
@discardableResult
func add_relay(_ url: URL, info: RelayInfo) throws -> Relay {
func add_relay(_ url: URL, info: RelayInfo) throws {
let relay_id = get_relay_id(url)
if get_relay(relay_id) != nil {
throw RelayError.RelayAlreadyExists
@ -78,57 +94,40 @@ final class RelayPool {
}
let descriptor = RelayDescriptor(url: url, info: info)
let relay = Relay(descriptor: descriptor, connection: conn)
relays.append(relay)
return relay
self.relays.append(relay)
}
/// This is used to retry dead connections
func connect_to_disconnected() {
for relay in relays where !relay.is_broken && relay.connection.state != .connected {
for relay in relays {
let c = relay.connection
let is_connecting = c.state == .reconnecting || c.state == .connecting
let is_connecting = c.isReconnecting || c.isConnecting
let retry_attempts = retry_attempts_per_relay[c.url] ?? 0
let delay = pow(Constants.base_reconnect_delay, TimeInterval(retry_attempts + 1)) // the + 1 helps us avoid a 1-second delay for the first retry
if is_connecting && (Date.now.timeIntervalSince1970 - c.last_connection_attempt) > delay {
if retry_attempts > Constants.max_retry_attempts {
if c.state != .notConnected {
c.disconnect()
print("exceeded max connection attempts with \(relay.descriptor.url.absoluteString)")
relay.mark_broken()
}
continue
} else {
print("stale connection detected (\(relay.descriptor.url.absoluteString)). retrying after \(delay) seconds...")
c.connect(force: true)
retry_attempts_per_relay[c.url] = retry_attempts + 1
}
} else if is_connecting {
if is_connecting && (Date.now.timeIntervalSince1970 - c.last_connection_attempt) > 5 {
print("stale connection detected (\(relay.descriptor.url.absoluteString)). retrying...")
relay.connection.connect(force: true)
} else if relay.is_broken || is_connecting || c.isConnected {
continue
} else {
c.reconnect()
relay.connection.reconnect()
}
}
}
func reconnect(to relay_ids: [String]? = nil) {
let relays: [Relay]
if let relay_ids {
relays = get_relays(relay_ids)
} else {
relays = self.relays
}
for relay in relays where !relay.is_broken {
func reconnect(to: [String]? = nil) {
let relays = to.map{ get_relays($0) } ?? self.relays
for relay in relays {
// don't try to reconnect to broken relays
relay.connection.reconnect()
}
}
func mark_broken(_ relay_id: String) {
relays.first(where: { $0.id == relay_id })?.mark_broken()
for relay in relays {
relay.mark_broken()
}
}
func connect(to: [String]? = nil) {
@ -138,8 +137,8 @@ final class RelayPool {
}
}
private func disconnect(from: [String]? = nil) {
let relays = from.map{ get_relays($0) } ?? self.relays
func disconnect(to: [String]? = nil) {
let relays = to.map{ get_relays($0) } ?? self.relays
for relay in relays {
relay.connection.disconnect()
}
@ -147,23 +146,35 @@ final class RelayPool {
func unsubscribe(sub_id: String, to: [String]? = nil) {
if to == nil {
remove_handler(sub_id: sub_id)
self.remove_handler(sub_id: sub_id)
}
send(.unsubscribe(sub_id), to: to)
self.send(.unsubscribe(sub_id), to: to)
}
func subscribe_to(sub_id: String, filters: [NostrFilter], to: [String]? = nil, handler: @escaping (String, NostrConnectionEvent) -> ()) {
func subscribe(sub_id: String, filters: [NostrFilter], handler: @escaping (String, NostrConnectionEvent) -> (), to: [String]? = nil) {
register_handler(sub_id: sub_id, handler: handler)
send(.subscribe(.init(filters: filters, sub_id: sub_id)), to: to)
}
func subscribe_to(sub_id: String, filters: [NostrFilter], to: [String]?, handler: @escaping (String, NostrConnectionEvent) -> ()) {
register_handler(sub_id: sub_id, handler: handler)
send(.subscribe(.init(filters: filters, sub_id: sub_id)), to: to)
}
func count_queued(relay: String) -> Int {
request_queue.filter({ $0.relay == relay }).count
var c = 0
for request in request_queue {
if request.relay == relay {
c += 1
}
}
return c
}
func queue_req(r: NostrRequest, relay: String) {
let count = count_queued(relay: relay)
guard count < Constants.max_queued_requests else {
guard count <= 10 else {
print("can't queue, too many queued events for \(relay)")
return
}
@ -173,10 +184,10 @@ final class RelayPool {
}
func send(_ req: NostrRequest, to: [String]? = nil) {
let relays = to.map { get_relays($0) } ?? self.relays
let relays = to.map{ get_relays($0) } ?? self.relays
for relay in relays {
guard relay.connection.state == .connected else {
guard relay.connection.isConnected else {
queue_req(r: req, relay: relay.id)
continue
}
@ -196,14 +207,17 @@ final class RelayPool {
func record_last_pong(relay_id: String, event: NostrConnectionEvent) {
if case .ws_event(let ws_event) = event {
if case .pong = ws_event {
if let relay = relays.first(where: { $0.id == relay_id }) {
relay.last_pong = UInt32(Date.now.timeIntervalSince1970)
for relay in relays {
if relay.id == relay_id {
relay.last_pong = UInt32(Date.now.timeIntervalSince1970)
return
}
}
}
}
}
private func run_queue(_ relay_id: String) {
func run_queue(_ relay_id: String) {
self.request_queue = request_queue.reduce(into: Array<QueuedRequest>()) { (q, req) in
guard req.relay == relay_id else {
q.append(req)
@ -221,14 +235,17 @@ final class RelayPool {
let k = relay_id + nev.id
if !seen.contains(k) {
seen.insert(k)
let prev_count = counts[relay_id] ?? 0
counts[relay_id] = prev_count + 1
if counts[relay_id] == nil {
counts[relay_id] = 1
} else {
counts[relay_id] = (counts[relay_id] ?? 0) + 1
}
}
}
}
}
private func handle_event(relay_id: String, event: NostrConnectionEvent) {
func handle_event(relay_id: String, event: NostrConnectionEvent) {
record_last_pong(relay_id: relay_id, event: event)
record_seen(relay_id: relay_id, event: event)
@ -248,5 +265,7 @@ final class RelayPool {
func add_rw_relay(_ pool: RelayPool, _ url: String) {
let url_ = URL(string: url)!
let _ = try? pool.add_relay(url_, info: RelayInfo.rw)
try? pool.add_relay(url_, info: RelayInfo.rw)
}

View File

@ -7,16 +7,6 @@
import SwiftUI
extension RelayConnection.State {
var indicatorColor: Color {
switch self {
case .connected: return .green
case .connecting, .reconnecting: return .yellow
default: return .red
}
}
}
struct RelayStatus: View {
let pool: RelayPool
let relay: String
@ -26,10 +16,18 @@ struct RelayStatus: View {
@State var conn_color: Color = .gray
func update_connection_color() {
guard let relay = pool.relays.first(where: { $0.id == relay }) else {
return
for relay in pool.relays {
if relay.id == self.relay {
let c = relay.connection
if c.isConnected {
conn_color = .green
} else if c.isConnecting || c.isReconnecting {
conn_color = .yellow
} else {
conn_color = .red
}
}
}
conn_color = relay.connection.state.indicatorColor
}
var body: some View {

View File

@ -1,170 +0,0 @@
//
// RelayPoolTests.swift
// damusTests
//
// Created by Bryan Montz on 2/25/23.
//
import XCTest
@testable import damus
final class RelayPoolTests: XCTestCase {
private let fakeRelayURL = URL(string: "wss://some.relay.com")!
private func setUpPool() throws -> RelayPool {
let pool = RelayPool()
XCTAssertTrue(pool.relays.isEmpty)
try pool.add_relay(fakeRelayURL, info: RelayInfo.rw)
return pool
}
// MARK: - Relay Add/Remove
func testAddRelay() throws {
let pool = try setUpPool()
XCTAssertEqual(pool.relays.count, 1)
}
func testRejectDuplicateRelay() throws {
let pool = try setUpPool()
XCTAssertThrowsError(try pool.add_relay(fakeRelayURL, info: RelayInfo.rw)) { error in
XCTAssertEqual(error as? RelayError, RelayError.RelayAlreadyExists)
}
}
func testRemoveRelay() throws {
let pool = try setUpPool()
XCTAssertEqual(pool.relays.count, 1)
pool.remove_relay(fakeRelayURL.absoluteString)
XCTAssertTrue(pool.relays.isEmpty)
}
func testMarkRelayBroken() throws {
let pool = try setUpPool()
let relay = try XCTUnwrap(pool.relays.first(where: { $0.id == fakeRelayURL.absoluteString }))
XCTAssertFalse(relay.is_broken)
pool.mark_broken(fakeRelayURL.absoluteString)
XCTAssertTrue(relay.is_broken)
}
func testGetRelay() throws {
let pool = try setUpPool()
XCTAssertNotNil(pool.get_relay(fakeRelayURL.absoluteString))
}
func testGetRelays() throws {
let pool = try setUpPool()
try pool.add_relay(URL(string: "wss://second.relay.com")!, info: RelayInfo.rw)
let allRelays = pool.get_relays([fakeRelayURL.absoluteString, "wss://second.relay.com"])
XCTAssertEqual(allRelays.count, 2)
let relays = pool.get_relays(["wss://second.relay.com"])
XCTAssertEqual(relays.count, 1)
}
// MARK: - Handler Add/Remove
private func setUpPoolWithHandler(sub_id: String) -> RelayPool {
let pool = RelayPool()
XCTAssertTrue(pool.handlers.isEmpty)
pool.register_handler(sub_id: sub_id) { _, _ in }
return pool
}
func testAddHandler() {
let sub_id = "123"
let pool = setUpPoolWithHandler(sub_id: sub_id)
XCTAssertEqual(pool.handlers.count, 1)
}
func testRejectDuplicateHandler() {
let sub_id = "123"
let pool = setUpPoolWithHandler(sub_id: sub_id)
XCTAssertEqual(pool.handlers.count, 1)
pool.register_handler(sub_id: sub_id) { _, _ in }
XCTAssertEqual(pool.handlers.count, 1)
}
func testRemoveHandler() {
let sub_id = "123"
let pool = setUpPoolWithHandler(sub_id: sub_id)
XCTAssertEqual(pool.handlers.count, 1)
pool.remove_handler(sub_id: sub_id)
XCTAssertTrue(pool.handlers.isEmpty)
}
func testRecordLastPong() throws {
let pool = try setUpPool()
let relayId = fakeRelayURL.absoluteString
let relay = try XCTUnwrap(pool.get_relay(relayId))
XCTAssertEqual(relay.last_pong, 0)
let pongEvent = NostrConnectionEvent.ws_event(.pong(nil))
pool.record_last_pong(relay_id: relayId, event: pongEvent)
XCTAssertNotEqual(relay.last_pong, 0)
}
func testSeenAndCounts() throws {
let pool = try setUpPool()
XCTAssertTrue(pool.seen.isEmpty)
XCTAssertTrue(pool.counts.isEmpty)
let event = NostrEvent(id: "123", content: "", pubkey: "")
let connectionEvent = NostrConnectionEvent.nostr_event(NostrResponse.event("", event))
let relay_id = fakeRelayURL.absoluteString
pool.record_seen(relay_id: relay_id, event: connectionEvent)
XCTAssertTrue(pool.seen.contains("wss://some.relay.com123"))
XCTAssertEqual(pool.counts[relay_id], 1)
pool.record_seen(relay_id: relay_id, event: connectionEvent)
// don't count the same event twice
XCTAssertEqual(pool.counts[relay_id], 1)
}
func testAddQueuedRequest() throws {
let pool = try setUpPool()
XCTAssertEqual(pool.count_queued(relay: fakeRelayURL.absoluteString), 0)
let req = NostrRequest.unsubscribe("")
pool.queue_req(r: req, relay: fakeRelayURL.absoluteString)
XCTAssertEqual(pool.count_queued(relay: fakeRelayURL.absoluteString), 1)
}
func testRejectTooManyQueuedRequests() throws {
let pool = try setUpPool()
let maxRequests = RelayPool.Constants.max_queued_requests
for _ in 0..<maxRequests {
let req = NostrRequest.unsubscribe("")
pool.queue_req(r: req, relay: fakeRelayURL.absoluteString)
}
XCTAssertEqual(pool.count_queued(relay: fakeRelayURL.absoluteString), maxRequests)
// try to add one beyond the maximum
let req = NostrRequest.unsubscribe("")
pool.queue_req(r: req, relay: fakeRelayURL.absoluteString)
XCTAssertEqual(pool.count_queued(relay: fakeRelayURL.absoluteString), maxRequests)
}
}