fix: cache preload
feat: Fetch only newest relay list updates
This commit is contained in:
parent
2ccee7bd7c
commit
988416f353
@ -11,8 +11,8 @@ class DMCache extends FeedCache<RawEvent> {
|
||||
return of.id;
|
||||
}
|
||||
|
||||
override async preload(): Promise<void> {
|
||||
await super.preload();
|
||||
override async preload(follows?: Array<string>): Promise<void> {
|
||||
await super.preload(follows);
|
||||
// load all dms to memory
|
||||
await this.buffer([...this.onTable]);
|
||||
}
|
||||
|
@ -12,8 +12,8 @@ class EventInteractionCache extends FeedCache<EventInteraction> {
|
||||
return sha256(of.event + of.by);
|
||||
}
|
||||
|
||||
override async preload(): Promise<void> {
|
||||
await super.preload();
|
||||
override async preload(follows?: Array<string>): Promise<void> {
|
||||
await super.preload(follows);
|
||||
|
||||
const data = window.localStorage.getItem("zap-cache");
|
||||
if (data) {
|
||||
|
@ -12,18 +12,18 @@ interface HookFilter {
|
||||
|
||||
export default abstract class FeedCache<TCached> {
|
||||
#name: string;
|
||||
#table: Table<TCached>;
|
||||
#hooks: Array<HookFilter> = [];
|
||||
#snapshot: Readonly<Array<TCached>> = [];
|
||||
#changed = true;
|
||||
#hits = 0;
|
||||
#miss = 0;
|
||||
protected table: Table<TCached>;
|
||||
protected onTable: Set<string> = new Set();
|
||||
protected cache: Map<string, TCached> = new Map();
|
||||
|
||||
constructor(name: string, table: Table<TCached>) {
|
||||
this.#name = name;
|
||||
this.#table = table;
|
||||
this.table = table;
|
||||
setInterval(() => {
|
||||
debug(this.#name)(
|
||||
"%d loaded, %d on-disk, %d hooks, %d% hit",
|
||||
@ -35,9 +35,9 @@ export default abstract class FeedCache<TCached> {
|
||||
}, 30_000);
|
||||
}
|
||||
|
||||
async preload() {
|
||||
async preload(follows?: Array<string>) {
|
||||
if (db.ready) {
|
||||
const keys = await this.#table.toCollection().primaryKeys();
|
||||
const keys = await this.table.toCollection().primaryKeys();
|
||||
this.onTable = new Set<string>(keys.map(a => a as string));
|
||||
}
|
||||
}
|
||||
@ -75,7 +75,7 @@ export default abstract class FeedCache<TCached> {
|
||||
|
||||
async get(key?: string) {
|
||||
if (key && !this.cache.has(key) && db.ready) {
|
||||
const cached = await this.#table.get(key);
|
||||
const cached = await this.table.get(key);
|
||||
if (cached) {
|
||||
this.cache.set(this.key(cached), cached);
|
||||
this.notifyChange([key]);
|
||||
@ -88,7 +88,7 @@ export default abstract class FeedCache<TCached> {
|
||||
async bulkGet(keys: Array<string>) {
|
||||
const missing = keys.filter(a => !this.cache.has(a));
|
||||
if (missing.length > 0 && db.ready) {
|
||||
const cached = await this.#table.bulkGet(missing);
|
||||
const cached = await this.table.bulkGet(missing);
|
||||
cached.forEach(a => {
|
||||
if (a) {
|
||||
this.cache.set(this.key(a), a);
|
||||
@ -105,7 +105,7 @@ export default abstract class FeedCache<TCached> {
|
||||
const k = this.key(obj);
|
||||
this.cache.set(k, obj);
|
||||
if (db.ready) {
|
||||
await this.#table.put(obj);
|
||||
await this.table.put(obj);
|
||||
this.onTable.add(k);
|
||||
}
|
||||
this.notifyChange([k]);
|
||||
@ -113,7 +113,7 @@ export default abstract class FeedCache<TCached> {
|
||||
|
||||
async bulkSet(obj: Array<TCached>) {
|
||||
if (db.ready) {
|
||||
await this.#table.bulkPut(obj);
|
||||
await this.table.bulkPut(obj);
|
||||
obj.forEach(a => this.onTable.add(this.key(a)));
|
||||
}
|
||||
obj.forEach(v => this.cache.set(this.key(v), v));
|
||||
@ -164,7 +164,7 @@ export default abstract class FeedCache<TCached> {
|
||||
key: a,
|
||||
}));
|
||||
const start = unixNowMs();
|
||||
const fromCache = await this.#table.bulkGet(mapped.filter(a => a.has).map(a => a.key));
|
||||
const fromCache = await this.table.bulkGet(mapped.filter(a => a.has).map(a => a.key));
|
||||
const fromCacheFiltered = fromCache.filter(a => a !== undefined).map(a => unwrap(a));
|
||||
fromCacheFiltered.forEach(a => {
|
||||
this.cache.set(this.key(a), a);
|
||||
@ -184,7 +184,7 @@ export default abstract class FeedCache<TCached> {
|
||||
}
|
||||
|
||||
async clear() {
|
||||
await this.#table.clear();
|
||||
await this.table.clear();
|
||||
this.cache.clear();
|
||||
this.onTable.clear();
|
||||
}
|
||||
|
@ -18,6 +18,14 @@ class UserProfileCache extends FeedCache<MetadataCache> {
|
||||
return of.pubkey;
|
||||
}
|
||||
|
||||
override async preload(follows?: Array<string>): Promise<void> {
|
||||
await super.preload(follows);
|
||||
// load follows profiles
|
||||
if (follows) {
|
||||
await this.buffer(follows);
|
||||
}
|
||||
}
|
||||
|
||||
async search(q: string): Promise<Array<MetadataCache>> {
|
||||
if (db.ready) {
|
||||
// on-disk cache will always have more data
|
||||
|
@ -10,6 +10,19 @@ class UsersRelaysCache extends FeedCache<UsersRelays> {
|
||||
return of.pubkey;
|
||||
}
|
||||
|
||||
override async preload(follows?: Array<string>): Promise<void> {
|
||||
await super.preload(follows);
|
||||
if (follows) {
|
||||
await this.buffer(follows);
|
||||
}
|
||||
}
|
||||
|
||||
newest(): number {
|
||||
let ret = 0;
|
||||
this.cache.forEach(v => (ret = v.created_at > ret ? v.created_at : ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
takeSnapshot(): Array<UsersRelays> {
|
||||
return [...this.cache.values()];
|
||||
}
|
||||
|
@ -52,11 +52,14 @@ export function mapEventToProfile(ev: RawEvent) {
|
||||
}
|
||||
}
|
||||
|
||||
export async function preload() {
|
||||
await UserCache.preload();
|
||||
await DmCache.preload();
|
||||
await InteractionCache.preload();
|
||||
await UserRelays.preload();
|
||||
export async function preload(follows?: Array<string>) {
|
||||
const preloads = [
|
||||
UserCache.preload(follows),
|
||||
DmCache.preload(follows),
|
||||
InteractionCache.preload(follows),
|
||||
UserRelays.preload(follows),
|
||||
];
|
||||
await Promise.all(preloads);
|
||||
}
|
||||
|
||||
export { UserCache, DmCache };
|
||||
|
@ -21,6 +21,7 @@ export interface RelayMetrics {
|
||||
|
||||
export interface UsersRelays {
|
||||
pubkey: HexKey;
|
||||
created_at: number;
|
||||
relays: FullRelaySettings[];
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { useEffect, useMemo } from "react";
|
||||
import { TaggedRawEvent, Lists, EventKind } from "@snort/nostr";
|
||||
import debug from "debug";
|
||||
|
||||
import { bech32ToHex, getNewest, getNewestEventTagsByKey, unwrap } from "SnortUtils";
|
||||
import { makeNotification, sendNotification } from "Notifications";
|
||||
@ -41,6 +42,7 @@ export default function useLoginFeed() {
|
||||
.limit(1);
|
||||
|
||||
const dmSince = DmCache.newest();
|
||||
debug("LoginFeed")("Loading dms since %s", new Date(dmSince * 1000).toISOString());
|
||||
b.withFilter().authors([pubKey]).kinds([EventKind.DirectMessage]).since(dmSince);
|
||||
b.withFilter().kinds([EventKind.DirectMessage]).tag("p", [pubKey]).since(dmSince);
|
||||
return b;
|
||||
|
@ -1,20 +1,24 @@
|
||||
import { useMemo } from "react";
|
||||
import { HexKey, FullRelaySettings, TaggedRawEvent, RelaySettings, EventKind } from "@snort/nostr";
|
||||
import debug from "debug";
|
||||
|
||||
import { sanitizeRelayUrl } from "SnortUtils";
|
||||
import { PubkeyReplaceableNoteStore, RequestBuilder } from "System";
|
||||
import useRequestBuilder from "Hooks/useRequestBuilder";
|
||||
import { UserRelays } from "Cache/UserRelayCache";
|
||||
|
||||
interface RelayList {
|
||||
pubkey: string;
|
||||
created: number;
|
||||
created_at: number;
|
||||
relays: FullRelaySettings[];
|
||||
}
|
||||
|
||||
export default function useRelaysFeedFollows(pubkeys: HexKey[]): Array<RelayList> {
|
||||
const sub = useMemo(() => {
|
||||
const b = new RequestBuilder(`relays:follows`);
|
||||
b.withFilter().authors(pubkeys).kinds([EventKind.Relays, EventKind.ContactList]);
|
||||
const since = UserRelays.newest();
|
||||
debug("LoginFeed")("Loading relay lists since %s", new Date(since * 1000).toISOString());
|
||||
b.withFilter().authors(pubkeys).kinds([EventKind.Relays, EventKind.ContactList]).since(since);
|
||||
return b;
|
||||
}, [pubkeys]);
|
||||
|
||||
@ -22,7 +26,7 @@ export default function useRelaysFeedFollows(pubkeys: HexKey[]): Array<RelayList
|
||||
return notes.map(ev => {
|
||||
return {
|
||||
pubkey: ev.pubkey,
|
||||
created: ev.created_at,
|
||||
created_at: ev.created_at,
|
||||
relays: ev.tags
|
||||
.map(a => {
|
||||
return {
|
||||
@ -45,7 +49,7 @@ export default function useRelaysFeedFollows(pubkeys: HexKey[]): Array<RelayList
|
||||
const relays: Record<string, RelaySettings> = JSON.parse(ev.content);
|
||||
return {
|
||||
pubkey: ev.pubkey,
|
||||
created: ev.created_at,
|
||||
created_at: ev.created_at,
|
||||
relays: Object.entries(relays)
|
||||
.map(([k, v]) => {
|
||||
return {
|
||||
@ -61,7 +65,7 @@ export default function useRelaysFeedFollows(pubkeys: HexKey[]): Array<RelayList
|
||||
}
|
||||
return {
|
||||
pubkey: ev.pubkey,
|
||||
created: 0,
|
||||
created_at: 0,
|
||||
relays: [],
|
||||
};
|
||||
});
|
||||
|
@ -14,9 +14,7 @@ import useLoginFeed from "Feed/LoginFeed";
|
||||
import { totalUnread } from "Pages/MessagesPage";
|
||||
import useModeration from "Hooks/useModeration";
|
||||
import { NoteCreator } from "Element/NoteCreator";
|
||||
import { db } from "Db";
|
||||
import useEventPublisher from "Feed/EventPublisher";
|
||||
import { preload } from "Cache";
|
||||
import { useDmCache } from "Hooks/useDmsCache";
|
||||
import { mapPlanName } from "./subscribe";
|
||||
import useLogin from "Hooks/useLogin";
|
||||
@ -111,29 +109,6 @@ export default function Layout() {
|
||||
};
|
||||
}, [preferences.theme]);
|
||||
|
||||
useEffect(() => {
|
||||
// check DB support then init
|
||||
db.isAvailable().then(async a => {
|
||||
db.ready = a;
|
||||
if (a) {
|
||||
await preload();
|
||||
}
|
||||
console.debug(`Using db: ${a ? "IndexedDB" : "In-Memory"}`);
|
||||
|
||||
try {
|
||||
if ("registerProtocolHandler" in window.navigator) {
|
||||
window.navigator.registerProtocolHandler(
|
||||
"web+nostr",
|
||||
`${window.location.protocol}//${window.location.host}/%s`
|
||||
);
|
||||
console.info("Registered protocol handler for 'web+nostr'");
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Failed to register protocol handler", e);
|
||||
}
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className={pageClass}>
|
||||
{!shouldHideHeader && (
|
||||
|
@ -247,7 +247,7 @@ export class Query implements QueryBase {
|
||||
return false;
|
||||
}
|
||||
if ((q.relays?.length ?? 0) === 0 && c.Ephemeral) {
|
||||
this.#log("Cant send non-specific REQ to ephemeral connection");
|
||||
this.#log("Cant send non-specific REQ to ephemeral connection %o", q.relays);
|
||||
return false;
|
||||
}
|
||||
if (q.filters.some(a => a.search) && !c.SupportsNip(Nips.Search)) {
|
||||
|
@ -221,7 +221,7 @@ export class NostrSystem extends ExternalStore<SystemSnapshot> {
|
||||
if (rb.options?.leaveOpen) {
|
||||
q.leaveOpen = rb.options.leaveOpen;
|
||||
}
|
||||
if (rb.options?.relays) {
|
||||
if (rb.options?.relays && (rb.options?.relays?.length ?? 0) > 0) {
|
||||
q.relays = rb.options.relays;
|
||||
}
|
||||
|
||||
|
@ -32,6 +32,9 @@ import Thread from "Element/Thread";
|
||||
import { SubscribeRoutes } from "Pages/subscribe";
|
||||
import ZapPoolPage from "Pages/ZapPool";
|
||||
import DebugPage from "Pages/Debug";
|
||||
import { db } from "Db";
|
||||
import { preload } from "Cache";
|
||||
import { LoginStore } from "Login";
|
||||
|
||||
// @ts-ignore
|
||||
window.__webpack_nonce__ = "ZmlhdGphZiBzYWlkIHNub3J0LnNvY2lhbCBpcyBwcmV0dHkgZ29vZCwgd2UgbWFkZSBpdCE=";
|
||||
@ -42,6 +45,25 @@ export const router = createBrowserRouter([
|
||||
{
|
||||
element: <Layout />,
|
||||
errorElement: <ErrorPage />,
|
||||
loader: async () => {
|
||||
db.ready = await db.isAvailable();
|
||||
if (db.ready) {
|
||||
await preload(LoginStore.takeSnapshot().follows.item);
|
||||
}
|
||||
|
||||
try {
|
||||
if ("registerProtocolHandler" in window.navigator) {
|
||||
window.navigator.registerProtocolHandler(
|
||||
"web+nostr",
|
||||
`${window.location.protocol}//${window.location.host}/%s`
|
||||
);
|
||||
console.info("Registered protocol handler for 'web+nostr'");
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Failed to register protocol handler", e);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
children: [
|
||||
...RootRoutes,
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user