Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Batch account cache updates into one call #173

Merged
merged 2 commits into from
Jul 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
26 changes: 6 additions & 20 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 All @@ -64,7 +50,7 @@ export function ProgramChange(props: {
return (
<tr
onClick={() => 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' : ''
}`}
>
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