Skip to content

Commit

Permalink
refactor(e2e): use predefined addresses in load
Browse files Browse the repository at this point in the history
- use predefined set of addresses per network in wallet restoration test
  • Loading branch information
Ivaylo Andonov committed Mar 16, 2023
1 parent f0a951b commit 4537094
Show file tree
Hide file tree
Showing 11 changed files with 505,108 additions and 64 deletions.
7 changes: 7 additions & 0 deletions packages/e2e/src/defaults.ts
@@ -0,0 +1,7 @@
export const SECOND = 1000;
export const MINUTE = 60 * SECOND;

export const TX_TIMEOUT_DEFAULT = 7 * MINUTE;
export const SYNC_TIMEOUT_DEFAULT = 3 * MINUTE;
export const BALANCE_TIMEOUT_DEFAULT = 3 * MINUTE;
export const FAST_OPERATION_TIMEOUT_DEFAULT = 15 * SECOND;
3 changes: 2 additions & 1 deletion packages/e2e/src/environment.ts
Expand Up @@ -80,7 +80,7 @@ const validators = {
ASSET_PROVIDER_PARAMS: providerParams(),
CHAIN_HISTORY_PROVIDER: str(),
CHAIN_HISTORY_PROVIDER_PARAMS: providerParams(),
DB_SYNC_CONNECTION_STRING: str(),
DB_SYNC_CONNECTION_STRING: str({ default: undefined }),
KEY_MANAGEMENT_PARAMS: keyManagementParams(),
KEY_MANAGEMENT_PROVIDER: str(),
LOGGER_MIN_SEVERITY: str({ default: 'info' }),
Expand All @@ -101,6 +101,7 @@ const validators = {
UTXO_PROVIDER_PARAMS: providerParams(),
VIRTUAL_USERS_COUNT: num(),
VIRTUAL_USERS_GENERATE_DURATION: num(),
WALLET_SYNC_TIMEOUT_IN_MS: num({ default: undefined }),
WORKER_PARALLEL_TRANSACTION: num()
} as const;

Expand Down
1 change: 1 addition & 0 deletions packages/e2e/src/index.ts
Expand Up @@ -2,5 +2,6 @@ export * from './factories';
export * from './FaucetProvider';
export * from './util';
export * from './environment';
export * from './defaults';
export * from './measurement-util';
export * from './load-test-scheduler';
97 changes: 64 additions & 33 deletions packages/e2e/test/artillery/wallet-restoration/WalletRestoration.ts
@@ -1,8 +1,9 @@
/* eslint-disable max-len */
import { AddressType, GroupedAddress, util } from '@cardano-sdk/key-management';
import { AddressesModel, WalletVars } from './types';
import { Cardano } from '@cardano-sdk/core';
import { FunctionHook } from '../artillery';
import { Pool, QueryResult } from 'pg';
import { Pool } from 'pg';
import { StubKeyAgent, getEnv, getWallet, walletVariables } from '../../../src';
import { findAddressesWithRegisteredStakeKey } from './queries';
import { logger } from '@cardano-sdk/util-dev';
Expand All @@ -12,11 +13,57 @@ const env = getEnv([
...walletVariables,
'DB_SYNC_CONNECTION_STRING',
'ARRIVAL_PHASE_DURATION_IN_SECS',
'VIRTUAL_USERS_COUNT'
'VIRTUAL_USERS_COUNT',
'WALLET_SYNC_TIMEOUT_IN_MS'
]);

const operationName = 'wallet-restoration';
const SHORTAGE_OF_WALLETS_FOUND_ERROR_MESSAGE = 'Addresses found from db are less than desired wallets count';
const syncTimeout = env.WALLET_SYNC_TIMEOUT_IN_MS;

const getNetworkName = ({ networkMagic }: Cardano.ChainId) => {
switch (networkMagic) {
case Cardano.NetworkMagics.Mainnet:
return 'mainnet';
case Cardano.NetworkMagics.Preprod:
return 'preprod';
default:
}
};

/**
* Extract addresses from predefined dump if specified network exists,
* otherwise fallback extraction using DB query (mainly to support local network env).
*
* @param {number} count Number of addresses to extract
* @returns {AddressesModel[]} Addresses subset
*/
const extractAddresses = async (count: number): Promise<AddressesModel[]> => {
let result: AddressesModel[] = [];
const network = getNetworkName(env.KEY_MANAGEMENT_PARAMS.chainId);
try {
if (network) {
const dump = require(`../../dump/addresses/${network}.json`);
if (dump.length < count) {
throw new Error(`You can not restore more than ${dump.length} distinct wallets for ${network} network.`);
}
result = dump.slice(0, count);
logger.info(`Selected subset of predefined ${network} addresses with count: ${result.length}.`);
} else if (env.DB_SYNC_CONNECTION_STRING) {
const db = new Pool({ connectionString: env.DB_SYNC_CONNECTION_STRING });
logger.info('About to query db for distinct addresses.');
result = (await db.query(findAddressesWithRegisteredStakeKey, [count])).rows;
if (result.length < count) {
throw new Error(`Addresses found from db are less than desired wallets count of ${count}`);
}
logger.info(`Found DB addresses count: ${result.length}`);
} else {
throw new Error('Please provide a valid KEY_MANAGEMENT_PARAMS or DB_SYNC_CONNECTION_STRING env variable.');
}
} catch (error) {
throw new Error(`Error was thrown while extracting addresses due to: ${error}`);
}
return result;
};

const mapToGroupedAddress = (addrModel: AddressesModel): GroupedAddress => ({
accountIndex: 0,
Expand All @@ -27,31 +74,10 @@ const mapToGroupedAddress = (addrModel: AddressesModel): GroupedAddress => ({
type: AddressType.External
});

export const findAddresses: FunctionHook<WalletVars> = async ({ vars }, ee, done) => {
vars.walletsCount = Number(env.VIRTUAL_USERS_COUNT);
const db: Pool = new Pool({ connectionString: env.DB_SYNC_CONNECTION_STRING });

try {
logger.info('About to query db for distinct addresses');
const result: QueryResult<AddressesModel> = await db.query(findAddressesWithRegisteredStakeKey, [
vars.walletsCount
]);
logger.info('Found addresses count', result.rowCount);
logger.info(
'Found addresses',
result.rows.map(({ address }) => address)
);

vars.addresses = result.rows.map(mapToGroupedAddress);
} catch (error) {
ee.emit('counter', 'findAddresses.error', 1);
logger.error('Error thrown while performing findAddresses db sync query', error);
}

if (vars.addresses.length < vars.walletsCount) {
logger.error(SHORTAGE_OF_WALLETS_FOUND_ERROR_MESSAGE);
throw new Error(SHORTAGE_OF_WALLETS_FOUND_ERROR_MESSAGE);
}
export const getAddresses: FunctionHook<WalletVars> = async ({ vars }, _, done) => {
vars.walletLoads = Number(env.VIRTUAL_USERS_COUNT);
const result = await extractAddresses(vars.walletLoads);
vars.addresses = result.map(mapToGroupedAddress);
done();
};

Expand All @@ -62,10 +88,11 @@ let index = 0;

export const walletRestoration: FunctionHook<WalletVars> = async ({ vars, _uid }, ee, done) => {
const currentAddress = vars.addresses[index];
logger.info('Current address:', currentAddress.address);
logger.info(`Current address: ${currentAddress.address}`);
++index;

try {
// Creates Stub KeyAgent
const keyAgent = util.createAsyncKeyAgent(new StubKeyAgent(currentAddress));

// Start to measure wallet restoration time
Expand All @@ -79,24 +106,28 @@ export const walletRestoration: FunctionHook<WalletVars> = async ({ vars, _uid }
polling: { interval: 50 }
});

await waitForWalletStateSettle(wallet);
vars.currentWallet = wallet;
await waitForWalletStateSettle(wallet, syncTimeout);

// Emit custom metrics
ee.emit('histogram', `${operationName}.time`, Date.now() - startedAt);
ee.emit('counter', operationName, 1);

logger.info(`Wallet with name ${wallet.name} was successfully restored`);
logger.info(
`Wallet with name ${vars.currentWallet.name} and address ${currentAddress.address} was successfully restored`
);
} catch (error) {
ee.emit('counter', `${operationName}.error`, 1);
logger.error(error);
logger.error(
`Error was thrown while wallet restoration for ${vars.currentWallet.name} with address ${currentAddress.address} caused by: ${error}`
);
}

done();
};

export const shutdownWallet: FunctionHook<WalletVars> = async ({ vars, _uid }, _ee, done) => {
vars.currentWallet.shutdown();
vars.currentWallet?.shutdown();
logger.info(`Wallet with VU id ${_uid} was shutdown`);
done();
};
Expand Up @@ -13,8 +13,8 @@ config:
processor: "./WalletRestoration.ts"
before:
flow:
- log: "Find distinct addresses from DB"
- function: "findAddresses"
- log: "Get a subset of predefined addresses"
- function: "getAddresses"
after:
flow:
- log: "Load test scenario completed"
Expand Down
18 changes: 8 additions & 10 deletions packages/e2e/test/artillery/wallet-restoration/queries.ts
@@ -1,14 +1,12 @@
/**
* Query randomized distinct addresses from db associated with users who are staking.
* Query distinct addresses from db associated with users who are staking.
*/
export const findAddressesWithRegisteredStakeKey = `
SELECT * FROM (
SELECT DISTINCT txOut.address as address, sa.view as stake_address
FROM public.delegation d
LEFT JOIN public.tx_out txOut on
d.addr_id = txOut.stake_address_id
LEFT JOIN public.stake_address sa on
txOut.stake_address_id = sa.id
) distinct_addresses
ORDER BY RANDOM() LIMIT $1
SELECT COUNT(*) AS tx_count, address, sa.view as stake_address
FROM tx_out
LEFT JOIN public.stake_address sa
ON tx_out.stake_address_id = sa.id
WHERE tx_out.id stake_address_id IS NOT NULL
GROUP BY address, stake_address
ORDER BY address DESC LIMIT $1;
`;
3 changes: 2 additions & 1 deletion packages/e2e/test/artillery/wallet-restoration/types.ts
Expand Up @@ -5,12 +5,13 @@ import { SingleAddressWallet } from '@cardano-sdk/wallet';
* The context variables shared between all the hooks.
*/
export interface WalletVars {
walletsCount: number;
walletLoads: number;
addresses: GroupedAddress[];
currentWallet: SingleAddressWallet;
}

export interface AddressesModel {
tx_count: number;
address: string;
stake_address: string;
}

0 comments on commit 4537094

Please sign in to comment.