Skip to content

Commit

Permalink
Batch account cache updates into one call (#173)
Browse files Browse the repository at this point in the history
* Batch account cache updates into one call

* Remove excess items

Signed-off-by: Nathan LeClaire <[email protected]>
  • Loading branch information
nathanleclaire authored Jul 7, 2022
1 parent 08f056e commit 8369f35
Show file tree
Hide file tree
Showing 5 changed files with 37 additions and 72 deletions.
4 changes: 1 addition & 3 deletions src/renderer/components/AccountView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
24 changes: 5 additions & 19 deletions src/renderer/components/ProgramChange.tsx
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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
Expand Down
7 changes: 4 additions & 3 deletions src/renderer/components/ProgramChangeView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -120,20 +121,20 @@ 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);
}
});
setPinnedAccount(pinMap);
setDisplayList(showKeys);
refreshAccountInfos(net, showKeys);
}, 666);

const uniqueAccounts = displayList.length;
Expand Down
1 change: 0 additions & 1 deletion src/renderer/data/accounts/accountInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
73 changes: 27 additions & 46 deletions src/renderer/data/accounts/getAccount.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,6 @@ const cache = new LRUCache<string, AccountInfo>({
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);
};
Expand All @@ -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<AccountInfo | undefined> {
// 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,
Expand All @@ -68,9 +55,6 @@ export async function getAccount(
};
cache.set(`${net}_${pubKey}`, response);
return response;
// }

// return undefined;
}

export const truncateSolAmount = (solAmount: number | undefined) => {
Expand Down Expand Up @@ -119,32 +103,30 @@ export const getHumanName = (meta: AccountMetaValues | undefined) => {
return '';
};

export function getAccountNoWait(
net: Net,
pubKey: string
): AccountInfo | undefined {
// logger.silly('getAccount', { pubKey });
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));
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;
}
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)) {
Expand All @@ -155,12 +137,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);
}

Expand Down

0 comments on commit 8369f35

Please sign in to comment.