From fc984cafa6eaab2598e39f12eff2aab80b60f2ad Mon Sep 17 00:00:00 2001 From: Jonathan Staab Date: Sat, 4 Feb 2023 14:11:34 -0600 Subject: [PATCH] Clean up pool so we're not waiting for slow connections --- .eslintrc.cjs | 30 +++--- package-lock.json | Bin 266566 -> 288024 bytes package.json | 5 +- src/agent/index.ts | 24 +++-- src/agent/{pool.js => pool.ts} | 175 +++++++++++++-------------------- src/app/loaders.ts | 4 +- src/partials/Anchor.svelte | 1 + src/partials/Note.svelte | 1 - src/routes/Messages.svelte | 5 - src/routes/Person.svelte | 25 ++--- src/util/misc.ts | 3 - 11 files changed, 115 insertions(+), 158 deletions(-) rename src/agent/{pool.js => pool.ts} (51%) diff --git a/.eslintrc.cjs b/.eslintrc.cjs index bd809a07..79a6a723 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -1,26 +1,32 @@ module.exports = { - "env": { - "browser": true, - "es2021": true + root: true, + env: { + browser: true, + es2021: true }, - "plugins": [ - 'svelte3' - ], + plugins: ['svelte3', '@typescript-eslint'], overrides: [ { files: ['*.svelte'], processor: 'svelte3/svelte3' } ], - "extends": "eslint:recommended", - "parserOptions": { - "ecmaVersion": "latest", - "sourceType": "module" + extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'], + parser: '@typescript-eslint/parser', + parserOptions: { + ecmaVersion: "latest", + sourceType: "module", + tsconfigRootDir: __dirname, + project: ['./tsconfig.json'], + extraFileExtensions: ['.svelte'] }, - "rules": { + settings: { + 'svelte3/typescript': require('typescript'), + }, + rules: { "a11y-click-events-have-key-events": "off", "no-unused-vars": ["error", {args: "none"}], "no-async-promise-executor": "off", }, - "ignorePatterns": ["*.svg"] + ignorePatterns: ["*.svg"] } diff --git a/package-lock.json b/package-lock.json index 4edb0ecdb3cf144efad83f900dc9895ac5418394..e79124de82f84b9716205d64f52c0aee98bf9d8b 100644 GIT binary patch delta 11918 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 delta 1766 zcmciBOK2Q*9Kdn@GZQ46q)D1|x6LMLnl^3fNOpE+AG2bJ``($^clHf6-JRL&EBk(A zXYrO|FV)MSe-9RHMS4(4aM3>%p|qk9QOrT`Ch4IFJ}_Q9h*gs!f>*&@f0y5nFTc<0 zpO1fbdu#_Mlp>+WXjXYMehPjs;NwcGHUSS-h;ijRLgcfW=jFw|Ili#%D8p-nm(yF#2B($ z{O&O4EST3*R4U9S!x@$}q}w(F1dqWvW$(%q{AdeH_QsFgTClw6d$F zU<1R?M@zO$Fy8aC)tJwlq4I%fGLbKLb2+b0FY->4EOm+w&RwuLD^_<@wwgt6ObF#j zN#|&mNg=h_rNy4x)3BF)F>gW6u}vc%6yPlp*C=!O!NVK0{dYCx3w_Trhr_NGO!rsT zzCO{HsxPhe>w6E`!&-2B1{mSK6CVS#vNN<0U8T*A0_lv&zOLvgY&sgQUWt|Xq&4jz zb;i|dT@dhmMn2%?(*^(5X(hjFU84TS> zVlxlI*_{;7_Dd5kRZ5hcFW1|sKAJ2*P3axK3@ubZxWy0mWNq4mI zuvizLfmiN(4Q}Zrzes47O*X%gD=2yvv9cr z4Djj?Kn=s|U?G|aH^W6~oh%y58>Fl=xdl%!m1($iY>iIl(i9(TN%aVqq!U?VHO~hf zmS)<)^WI>mW+DBnwpt_B$#$efO3qY@wAf(-PKkQb1daU&9xR|&Ca~8AkT`|C&!DXk z#>}JL7WVVV@o|IHbxi$lZ-=jQ4W3vgM$k9cur+XO^5FgeepY!$Q$X9-vFbE>{%vd) zN3VX2%?_hacd@Mix-o^%kjF`H?kJB222?=(AN^HSa7Rs;{-FkMdx%T1|HYd1!zjKw ze@w)llu;i!BazUfsQqtNB=+5uBNsF{+h))Rg?JR*U Pq9jLra2idD1cChpwC6*F diff --git a/package.json b/package.json index 212dd396..ada718bb 100644 --- a/package.json +++ b/package.json @@ -12,12 +12,15 @@ }, "devDependencies": { "@sveltejs/vite-plugin-svelte": "^1.1.0", + "@typescript-eslint/eslint-plugin": "^5.50.0", + "@typescript-eslint/parser": "^5.50.0", "autoprefixer": "^10.4.13", - "eslint": "^8.28.0", + "eslint": "^8.33.0", "eslint-plugin-svelte3": "^4.0.0", "postcss": "^8.4.19", "svelte": "^3.52.0", "tailwindcss": "^3.2.4", + "typescript": "^4.9.5", "vite": "^3.2.3" }, "dependencies": { diff --git a/src/agent/index.ts b/src/agent/index.ts index a641d7b8..8d3cdd94 100644 --- a/src/agent/index.ts +++ b/src/agent/index.ts @@ -71,7 +71,7 @@ export const publish = async (relays, event) => { return signedEvent } -export const load = async (relays, filter, opts?) => { +export const load = async (relays, filter, opts?): Promise[]> => { const events = await pool.request(relays, filter, opts) await processEvents(events) @@ -79,18 +79,16 @@ export const load = async (relays, filter, opts?) => { return events } -export const listen = async (relays, filter, onEvent, {shouldProcess = true}: any = {}) => { - const sub = await pool.subscribe(relays, filter) +export const listen = (relays, filter, onEvent, {shouldProcess = true}: any = {}) => { + return pool.subscribe(relays, filter, { + onEvent: e => { + if (shouldProcess) { + processEvents(e) + } - sub.onEvent(e => { - if (shouldProcess) { - processEvents(e) - } - - if (onEvent) { - onEvent(e) - } + if (onEvent) { + onEvent(e) + } + }, }) - - return sub } diff --git a/src/agent/pool.js b/src/agent/pool.ts similarity index 51% rename from src/agent/pool.js rename to src/agent/pool.ts index a01b4f71..1a741667 100644 --- a/src/agent/pool.js +++ b/src/agent/pool.ts @@ -1,5 +1,6 @@ +import type {Relay} from 'nostr-tools' import {relayInit} from 'nostr-tools' -import {uniqBy, reject, prop, find, whereEq, is, filter, identity} from 'ramda' +import {uniqBy, reject, prop, find, whereEq, is} from 'ramda' import {ensurePlural} from 'hurdak/lib/hurdak' import {isRelay} from 'src/util/nostr' import {sleep} from 'src/util/misc' @@ -8,6 +9,12 @@ import {db} from 'src/agent/data' let connections = [] class Connection { + promise: Promise + nostr: Relay + status: string + url: string + stats: Record + lastRequest: number constructor(url) { this.promise = null this.nostr = this.init(url) @@ -107,7 +114,7 @@ const describeFilter = ({kinds = [], ...filter}) => { return '(' + parts.join(',') + ')' } -const subscribe = async (relays, filters) => { +const subscribe = async (relays, filters, {onEvent, onEose}: Record void>) => { relays = uniqBy(prop('url'), relays.filter(r => isRelay(r.url))) filters = ensurePlural(filters) @@ -117,151 +124,101 @@ const subscribe = async (relays, filters) => { filters.map(describeFilter).join(':'), ].join('-') + // Deduplicate events const seen = new Set() - const eose = [] - const events = [] - let onEvent - let onEose - const subs = filter(identity, await Promise.all( - relays.map(async relay => { - const conn = await connect(relay.url) + // Don't await before returning so we're not blocking on slow connects + const promises = relays.map(async relay => { + const conn = await connect(relay.url) - // If the relay failed to connect, give up - if (!conn) { - return null - } + // If the relay failed to connect, give up + if (!conn) { + return null + } - const sub = conn.nostr.sub(filters, {id}) + const sub = conn.nostr.sub(filters, {id}) - // Subscribe to events immediately so we don't miss any while we - // wait for slow relays to connect + if (onEvent) { sub.on('event', e => { if (!seen.has(e.id)) { - e.seen_on = sub.conn.url seen.add(e.id) - if (onEvent) { - onEvent(e) - } else { - events.push(e) - } + onEvent(Object.assign(e, {seen_on: conn.url})) } }) + } - // Same thing for eose - sub.on('eose', () => { - if (onEose) { - onEose(conn.url) - } else { - eose.push(conn.url) - } - }) + if (onEose) { + sub.on('eose', () => onEose(conn.url)) + } - sub.conn = conn - sub.conn.stats.activeCount += 1 + conn.stats.activeCount += 1 - if (sub.conn.stats.activeCount > 10) { - console.warn(`Relay ${sub.conn.url} has >10 active subscriptions`) - } + if (conn.stats.activeCount > 10) { + console.warn(`Relay ${conn.url} has >10 active subscriptions`) + } - return sub - }) - )) + return Object.assign(sub, {conn}) + }) return { - subs, unsub: () => { - subs.forEach(sub => { - if (sub.conn.status === 'ready') { - sub.unsub() + promises.forEach(async promise => { + const sub = await promise + + if (sub) { + if (sub.conn.status === 'ready') { + sub.unsub() + } + + sub.conn.stats.activeCount -= 1 } - - sub.conn.stats.activeCount -= 1 }) }, - onEvent: cb => { - // Report our buffered events - events.splice(0).forEach(e => cb(e)) - - // Add our listener for future ones - onEvent = cb - }, - onEose: cb => { - // Report our buffered eoses - eose.splice(0).forEach(e => cb(e)) - - // Add our listener for future ones - onEose = cb - }, } } -const request = (relays, filters, {threshold = 1} = {}) => { - relays = uniqBy(prop('url'), relays.filter(r => isRelay(r.url))) - +const request = (relays, filters, {threshold = 1} = {}): Promise[]> => { return new Promise(async resolve => { - const agg = await subscribe(relays, filters) + relays = uniqBy(prop('url'), relays.filter(r => isRelay(r.url))) + threshold = Math.min(relays.length, threshold) + const now = Date.now() const relaysWithEvents = new Set() const events = [] const eose = [] const attemptToComplete = () => { - // If we have all relays, more than `threshold` reporting events, most after - // a short timeout, or all after a long timeout, go ahead and unsubscribe. - const done = ( - eose.length === agg.subs.length - || eose.filter(url => relaysWithEvents.has(url)).length >= threshold - || ( - Date.now() - now >= 1000 - && eose.length > agg.subs.length - Math.round(agg.subs.length / 10) - ) - || Date.now() - now >= 5000 - ) - - if (done) { + if (eose.length >= threshold || Date.now() - now >= 5000) { agg.unsub() resolve(events) - - // Keep track of relay timeouts - agg.subs.forEach(async sub => { - if (!eose.includes(sub.conn.url)) { - const conn = findConnection(sub.conn.url) - - if (conn) { - conn.stats.count += 1 - conn.stats.timer += Date.now() - now - conn.stats.timeouts += 1 - } - } - }) } } - agg.onEvent(e => { - relaysWithEvents.add(e.seen_on) - events.push(e) - }) - - agg.onEose(async url => { - if (!eose.includes(url)) { - eose.push(url) - - const conn = findConnection(url) - - // Keep track of relay timing stats - if (conn) { - conn.stats.count += 1 - conn.stats.timer += Date.now() - now - } - } - - attemptToComplete() - }) - // If a relay takes too long, give up setTimeout(attemptToComplete, 5000) + + const agg = await subscribe(relays, filters, { + onEvent: e => { + relaysWithEvents.add(e.seen_on) + events.push(e) + }, + onEose: async url => { + if (!eose.includes(url)) { + eose.push(url) + + const conn = findConnection(url) + + // Keep track of relay timing stats + if (conn) { + conn.stats.count += 1 + conn.stats.timer += Date.now() - now + } + } + + attemptToComplete() + }, + }) }) } diff --git a/src/app/loaders.ts b/src/app/loaders.ts index 8dc28081..7a98dcbb 100644 --- a/src/app/loaders.ts +++ b/src/app/loaders.ts @@ -30,7 +30,7 @@ const loadPeople = (relays, pubkeys, {kinds = personKinds, force = false, ...opt const loadNetwork = async (relays, pubkey) => { // Get this user's profile to start with. This may update what relays // are available, so don't assign relays to a variable here. - let events = pubkey ? await loadPeople(relays, [pubkey], {force: true}) : [] + const events = pubkey ? await loadPeople(relays, [pubkey], {force: true}) : [] let petnames = Tags.from(events.filter(e => e.kind === 3)).type("p").all() // Default to some cool guys we know @@ -58,7 +58,7 @@ const loadContext = async (relays, notes, {loadParents = false, depth = 0, ...op const parentTags = uniq(chunk.map(findReply).filter(identity)) const parentIds = Tags.wrap(parentTags).values().all() const combinedRelays = uniq(relays.concat(Tags.wrap(parentTags).relays())) - const filter = [{kinds: [1, 7], '#e': chunkIds} as {}] + const filter = [{kinds: [1, 7], '#e': chunkIds} as object] if (authors.length > 0) { filter.push({kinds: personKinds, authors}) diff --git a/src/partials/Anchor.svelte b/src/partials/Anchor.svelte index 18c40681..b3a4285d 100644 --- a/src/partials/Anchor.svelte +++ b/src/partials/Anchor.svelte @@ -16,6 +16,7 @@ switcher(type, { anchor: "underline", button: "py-2 px-4 rounded bg-white text-accent", + 'button-circle': "w-10 h-10 flex justify-center items-center rounded-full bg-white text-accent", 'button-accent': "py-2 px-4 rounded bg-accent text-white", }), ) diff --git a/src/partials/Note.svelte b/src/partials/Note.svelte index aad26c87..d3fadf07 100644 --- a/src/partials/Note.svelte +++ b/src/partials/Note.svelte @@ -15,7 +15,6 @@ import Compose from "src/partials/Compose.svelte" import Card from "src/partials/Card.svelte" import {user, people, getPerson, getRelays, getEventRelays} from 'src/agent' - import {addRelay, removeRelay} from "src/app" import cmd from 'src/app/cmd' export let note diff --git a/src/routes/Messages.svelte b/src/routes/Messages.svelte index 0e28acc5..d35c5155 100644 --- a/src/routes/Messages.svelte +++ b/src/routes/Messages.svelte @@ -44,11 +44,6 @@ ) const loadMessages = async ({until, limit}) => { - const a = db.table('messages') - const b = a.where('pubkey') - const c = b.equals(pubkey) - const d = c.toArray() - const e = await d const fromThem = await db.table('messages').where('pubkey').equals(pubkey).toArray() const toThem = await db.table('messages').where('recipient').equals(pubkey).toArray() const events = fromThem.concat(toThem).filter(e => e.created_at < until) diff --git a/src/routes/Person.svelte b/src/routes/Person.svelte index 47e83960..3bcd5289 100644 --- a/src/routes/Person.svelte +++ b/src/routes/Person.svelte @@ -10,7 +10,6 @@ import Tabs from "src/partials/Tabs.svelte" import Content from "src/partials/Content.svelte" import Anchor from "src/partials/Anchor.svelte" - import Button from "src/partials/Button.svelte" import Spinner from "src/partials/Spinner.svelte" import Notes from "src/views/person/Notes.svelte" import Likes from "src/views/person/Likes.svelte" @@ -92,7 +91,7 @@
@@ -107,22 +106,24 @@
{/if}
-
+
{#if $user?.pubkey === pubkey && keys.canSign()} - - Edit profile - + Edit profile {:else if $user && keys.canSign()} - - + + - - + + {#if following} - + + + {:else} - + + + {/if} {/if}
diff --git a/src/util/misc.ts b/src/util/misc.ts index 3ed85fbd..2bc975d0 100644 --- a/src/util/misc.ts +++ b/src/util/misc.ts @@ -100,7 +100,6 @@ export const createScroller = (loadMore, {reverse = false} = {}) => { // loadMore function is not properly awaiting all the work necessary. // That is the problem, but see commit 8371fde for another strategy let done = false - let timeout = null const check = async () => { // While we have empty space, fill it const {scrollY, innerHeight} = window @@ -111,8 +110,6 @@ export const createScroller = (loadMore, {reverse = false} = {}) => { // Only trigger loading the first time we reach the threshold if (shouldLoad) { - clearTimeout(timeout) - await loadMore() }