From 98309dde794bb41f443da920ebc93c0bedb29c80 Mon Sep 17 00:00:00 2001 From: Nathan LeClaire Date: Wed, 6 Jul 2022 14:57:14 -0700 Subject: [PATCH 1/2] Batch account cache updates into one call --- src/renderer/components/AccountView.tsx | 4 +- src/renderer/components/ProgramChange.tsx | 26 ++----- src/renderer/components/ProgramChangeView.tsx | 7 +- src/renderer/data/accounts/accountInfo.ts | 1 - src/renderer/data/accounts/getAccount.ts | 67 +++++++++---------- 5 files changed, 41 insertions(+), 64 deletions(-) diff --git a/src/renderer/components/AccountView.tsx b/src/renderer/components/AccountView.tsx index 3d975f34..9535d578 100644 --- a/src/renderer/components/AccountView.tsx +++ b/src/renderer/components/AccountView.tsx @@ -120,9 +120,7 @@ function AccountView(props: { pubKey: string | undefined }) { return; } if (pubKey) { - getAccount(net, pubKey) - .then((a) => setSelectedAccountInfo(a)) - .catch(logger.info); + setSelectedAccountInfo(getAccount(net, pubKey)); } else { setSelectedAccountInfo(undefined); } diff --git a/src/renderer/components/ProgramChange.tsx b/src/renderer/components/ProgramChange.tsx index 82b3d10f..24ed7433 100644 --- a/src/renderer/components/ProgramChange.tsx +++ b/src/renderer/components/ProgramChange.tsx @@ -1,5 +1,4 @@ import { useCallback, useEffect, useState } from 'react'; -import { logger } from '@/common/globals'; import { setSelected } from '@/data/SelectedAccountsList/selectedAccountsState'; import { AccountInfo } from '../data/accounts/accountInfo'; import { useAccountMeta } from '../data/accounts/accountState'; @@ -33,27 +32,14 @@ export function ProgramChange(props: { if (status !== NetStatus.Running) { return; } - if (pubKey) { - getAccount(net, pubKey) - .then((res) => { - // eslint-disable-next-line promise/always-return - if (res) { - setChangeInfo(res); - } - }) - .catch(logger.info); - } else { + if (!pubKey) { setChangeInfo(undefined); + return; } + setChangeInfo(getAccount(net, pubKey)); }, [net, status, pubKey]); - - useEffect(() => { - updateAccount(); - }, [net, pubKey, updateAccount]); - - useInterval(() => { - updateAccount(); - }, 666); + useEffect(updateAccount, [updateAccount]); + useInterval(updateAccount, 666); const showCount = change?.count || 0; const showSOL = change @@ -64,7 +50,7 @@ export function ProgramChange(props: { return ( dispatch(setSelected(pubKey))} - className={`transition duration-50 bg-opacity-20 hover:bg-opacity-30 hover:bg-primary-light ${ + className={`transition cursor-pointer duration-50 bg-opacity-20 hover:bg-opacity-30 hover:bg-primary-light ${ selected ? 'bg-primary-light' : '' }`} > diff --git a/src/renderer/components/ProgramChangeView.tsx b/src/renderer/components/ProgramChangeView.tsx index c7951eaa..f404b99c 100644 --- a/src/renderer/components/ProgramChangeView.tsx +++ b/src/renderer/components/ProgramChangeView.tsx @@ -15,7 +15,8 @@ import createNewAccount from '../data/accounts/account'; import { AccountInfo } from '../data/accounts/accountInfo'; import { BASE58_PUBKEY_REGEX, - GetTopAccounts, + getTopAccounts, + refreshAccountInfos, } from '../data/accounts/getAccount'; import { subscribeProgramChanges, @@ -120,13 +121,12 @@ function ProgramChangeView() { } }); - const changes = GetTopAccounts( + const changes = getTopAccounts( net, MAX_PROGRAM_CHANGES_DISPLAYED, sortFunction ); - // logger.info('GetTopAccounts', changes); changes.forEach((key: string) => { if (!(key in pinMap)) { showKeys.push(key); @@ -134,6 +134,7 @@ function ProgramChangeView() { }); setPinnedAccount(pinMap); setDisplayList(showKeys); + refreshAccountInfos(net, showKeys); }, 666); const uniqueAccounts = displayList.length; diff --git a/src/renderer/data/accounts/accountInfo.ts b/src/renderer/data/accounts/accountInfo.ts index a9239c75..bd7c136a 100644 --- a/src/renderer/data/accounts/accountInfo.ts +++ b/src/renderer/data/accounts/accountInfo.ts @@ -11,7 +11,6 @@ export interface AccountInfo { accountId: sol.PublicKey; pubKey: string; net?: Net; - decodedData: string; // updatedSlot: number // this should be used to update old account info // info from program Changes diff --git a/src/renderer/data/accounts/getAccount.ts b/src/renderer/data/accounts/getAccount.ts index a8bfa4f3..489d2bb4 100644 --- a/src/renderer/data/accounts/getAccount.ts +++ b/src/renderer/data/accounts/getAccount.ts @@ -19,15 +19,6 @@ const cache = new LRUCache({ entryExpirationTimeInMS: 60000, }); -export const getAllAccounts = (): AccountInfo[] => { - const list: AccountInfo[] = []; - // eslint-disable-next-line no-restricted-syntax - for (const v of cache.values()) { - list.push(v); - } - return list; -}; - export const updateCache = (account: AccountInfo) => { cache.set(`${account.net}_${account.pubKey}`, account); }; @@ -39,27 +30,23 @@ export function peekAccount(net: Net, pubKey: string): AccountInfo | null { // This will always use, and update the lastUsed time any account in the cache. // if you absoluetly need to current latest, don't use this function :) // it is written to avoid RPC requests if at all possible, and is used in conjunction with the programChanges subscriptions -export async function getAccount( - net: Net, - pubKey: string -): Promise { - // logger.silly('getAccount', { pubKey }); +export function getAccount(net: Net, pubKey: string): AccountInfo | undefined { const cachedResponse = cache.get(`${net}_${pubKey}`); if (cachedResponse) { return cachedResponse; } - const solConn = new sol.Connection(netToURL(net)); - const key = new sol.PublicKey(pubKey); - const solAccount = await solConn.getAccountInfo(key); + logger.silly('getAccountInfo cache miss', pubKey, pubKey.toString()); - logger.silly('getAccountInfo cache miss', pubKey, key.toString()); - // TODO: these should not be made individually, instead, toss them on a list, and make getMultipleAccounts call once every 333mS or something - // if (solAccount) { const response: AccountInfo = { - accountId: key, - accountInfo: solAccount, - pubKey: key.toString(), + accountId: new sol.PublicKey(pubKey), + accountInfo: { + executable: false, + lamports: 0, + owner: sol.SystemProgram.programId, + data: undefined, + }, + pubKey: pubKey.toString(), net, count: 0, solDelta: 0, @@ -68,9 +55,6 @@ export async function getAccount( }; cache.set(`${net}_${pubKey}`, response); return response; - // } - - // return undefined; } export const truncateSolAmount = (solAmount: number | undefined) => { @@ -123,7 +107,6 @@ export function getAccountNoWait( net: Net, pubKey: string ): AccountInfo | undefined { - // logger.silly('getAccount', { pubKey }); const cachedResponse = cache.peek(`${net}_${pubKey}`); if (cachedResponse) { return cachedResponse; @@ -131,20 +114,31 @@ export function getAccountNoWait( return undefined; } +export async function refreshAccountInfos(net: Net, keys: string[]) { + const solConn = new sol.Connection(netToURL(net)); + const pubKeys = keys.map((k) => new sol.PublicKey(k)); + const accountInfos = await solConn.getMultipleAccountsInfo(pubKeys); + accountInfos.forEach((info, i) => { + const cachedAccount = cache.get(`${net}_${keys[i]}`); + if (!cachedAccount) { + logger.silly('cache miss', `${net}_${keys[i]}`); + return; + } + logger.silly('cache hit', `${net}_${keys[i]}`); + cachedAccount.accountInfo = info; + cache.set(`${net}_${keys[i]}`, cachedAccount); + }); +} + // Please note, yes, this is the shittest way to do sorting, but that's not the primary bottleneck -export function GetTopAccounts( +export function getTopAccounts( net: Net, count: number, sortFunction: (a: AccountInfo, b: AccountInfo) => number ): string[] { const top: string[] = []; - - // top.push('9u5DkG65FkxkjzP84JyuhfA7PhZB2SVxMKev4yVLjAd7'); - // 8FjV5PXabcMVuWnCtwrwiaSZHEix6FNrR48AU1zWbxjZ - - // logger.info('cached size', cache.size); - const keys: AccountInfo[] = []; + // eslint-disable-next-line no-restricted-syntax for (const key of cache.keys()) { if (key.startsWith(net)) { @@ -155,12 +149,11 @@ export function GetTopAccounts( } } } - // logger.info('sorting cached accounts', keys.length); + keys.sort(sortFunction); + // eslint-disable-next-line no-restricted-syntax for (const account of keys.slice(0, count - 1)) { - // logger.info('push', account.pubKey); - top.push(account.pubKey); } From 99cacb653809d0b6f07667f84b320371180dac82 Mon Sep 17 00:00:00 2001 From: Nathan LeClaire Date: Wed, 6 Jul 2022 15:42:53 -0700 Subject: [PATCH 2/2] Remove excess items Signed-off-by: Nathan LeClaire --- src/renderer/data/accounts/getAccount.ts | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/renderer/data/accounts/getAccount.ts b/src/renderer/data/accounts/getAccount.ts index 489d2bb4..f1ce64b7 100644 --- a/src/renderer/data/accounts/getAccount.ts +++ b/src/renderer/data/accounts/getAccount.ts @@ -103,17 +103,6 @@ export const getHumanName = (meta: AccountMetaValues | undefined) => { return ''; }; -export function getAccountNoWait( - net: Net, - pubKey: string -): AccountInfo | undefined { - const cachedResponse = cache.peek(`${net}_${pubKey}`); - if (cachedResponse) { - return cachedResponse; - } - return undefined; -} - export async function refreshAccountInfos(net: Net, keys: string[]) { const solConn = new sol.Connection(netToURL(net)); const pubKeys = keys.map((k) => new sol.PublicKey(k)); @@ -124,7 +113,6 @@ export async function refreshAccountInfos(net: Net, keys: string[]) { logger.silly('cache miss', `${net}_${keys[i]}`); return; } - logger.silly('cache hit', `${net}_${keys[i]}`); cachedAccount.accountInfo = info; cache.set(`${net}_${keys[i]}`, cachedAccount); });