diff --git a/indexer/services/roundtable/__tests__/helpers/usernames-helper.test.ts b/indexer/services/roundtable/__tests__/helpers/usernames-helper.test.ts index d5392c2771..b4d10aee69 100644 --- a/indexer/services/roundtable/__tests__/helpers/usernames-helper.test.ts +++ b/indexer/services/roundtable/__tests__/helpers/usernames-helper.test.ts @@ -2,52 +2,65 @@ import { generateUsernameForSubaccount, } from '../../src/helpers/usernames-helper'; -describe('usernames-helper', () => { - it('Check result and determinism of username username', () => { - const addresses = [ - 'dydx1gf4xlnpulkyex74asxxhg9ye05r28cxdd69s9u', - 'dydx10fx7sy6ywd5senxae9dwytf8jxek3t2gcen2vs', - 'dydx1t72ww7qzdx5rjlpp6cq0cqy09qlsjj7e4kpuyt', - 'dydx1wau5mja7j7zdavtfq9lu7ejef05hm6ffenlcsn', - 'dydx168pjt8rkru35239fsqvz7rzgeclakp49zx3aum', - 'dydx1df84hz7y0dd3mrqcv3vrhw9wdttelul8edqmvp', - 'dydx16h7p7f4dysrgtzptxx2gtpt5d8t834g9dj830z', - 'dydx15u9tppy5e2pdndvlrvafxqhuurj9mnpdstzj6z', - 'dydx1q54yvrslnu0xp4drpde6f4e0k2ap9efss5hpsd', - 'dydx199tqg4wdlnu4qjlxchpd7seg454937hjrknju4', - 'dydx1n88uc38xhjgxzw9nwre4ep2c8ga4fjxc565lnf', - 'dydx1n88uc38xhjgxzw9nwre4ep2c8ga4fjxc575lnf', - ]; +const addresses = [ + 'dydx1gf4xlnpulkyex74asxxhg9ye05r28cxdd69s9u', + 'dydx10fx7sy6ywd5senxae9dwytf8jxek3t2gcen2vs', + 'dydx1wau5mja7j7zdavtfq9lu7ejef05hm6ffenlcsn', + 'dydx1df84hz7y0dd3mrqcv3vrhw9wdttelul8edqmvp', + 'dydx16h7p7f4dysrgtzptxx2gtpt5d8t834g9dj830z', + 'dydx15u9tppy5e2pdndvlrvafxqhuurj9mnpdstzj6z', + 'dydx1q54yvrslnu0xp4drpde6f4e0k2ap9efss5hpsd', + 'dydx1n88uc38xhjgxzw9nwre4ep2c8ga4fjxc565lnf', + 'dydx1n88uc38xhjgxzw9nwre4ep2c8ga4fjxc575lnf', + 'dydx199tqg4wdlnu4qjlxchpd7seg454937hjrknju4', + 'dydx1c05rgh22wg5pmaufnj8z77kla8fgrgkrth6hlj', + 'dydx1qvsqtewdxqdpj5gnkl8usqz3yplpkgs52wv4fy', +]; +describe('usernames-helper', () => { + it('Check result of generated username', () => { const expectedUsernames = [ - 'CushyHandVE6', - 'AmpleCubeLKI', - 'AwareFoodHGP', - 'LoudLandXWV', - 'MossyStraw2JJ', - 'BoldGapOGY', - 'ZoomEraQE0', - 'WiryFernLEC', - 'RudeFuel59E', - 'MacroMealFK5', - 'HappySnapWTT', - 'BumpyEdgeH5Y', + 'CurlyHallVE6', + 'AmpleCrownLKI', + 'LoneLawXWV', + 'BlueGapOGY', + 'ZippyElfQE0', + 'WindyFaxLEC', + 'RoyalFruit59E', + 'GreenSnowWTT', + 'BubblyEarH5Y', + 'LunarMatFK5', + 'SubtleDig25M', + 'HillyAccess1C7', ]; + const gotUserNames = []; for (let i = 0; i < addresses.length; i++) { const address = addresses[i]; - for (let j = 0; j < 10; j++) { - const names = new Set(); - for (let k = 0; k < 10; k++) { - const username: string = generateUsernameForSubaccount(address, 0, k); - if (k === 0) { - expect(username).toEqual(expectedUsernames[i]); - } - names.add(username); + const namesForOneAddress = new Set(); + for (let k = 0; k < 10; k++) { + const username: string = generateUsernameForSubaccount(address, 0, k); + if (k === 0) { + gotUserNames.push(username); } - // for same address, difference nonce should result in different username - expect(names.size).toEqual(10); + namesForOneAddress.add(username); + } + // for same address, difference nonce should result in different username + expect(namesForOneAddress.size).toEqual(10); + } + expect(gotUserNames).toEqual(expectedUsernames); + }); + + it('Check determinism of generated username', () => { + for (let i = 0; i < addresses.length; i++) { + const address = addresses[i]; + const namesForOneAddress = new Set(); + for (let k = 0; k < 10; k++) { + const username: string = generateUsernameForSubaccount(address, 0, 0); + namesForOneAddress.add(username); } + // for same address, difference nonce should result in different username + expect(namesForOneAddress.size).toEqual(1); } }); }); diff --git a/indexer/services/roundtable/__tests__/tasks/subaccount-username-generator.test.ts b/indexer/services/roundtable/__tests__/tasks/subaccount-username-generator.test.ts index cceb9fe631..e6785959c1 100644 --- a/indexer/services/roundtable/__tests__/tasks/subaccount-username-generator.test.ts +++ b/indexer/services/roundtable/__tests__/tasks/subaccount-username-generator.test.ts @@ -49,9 +49,9 @@ describe('subaccount-username-generator', () => { {}, [], {}); const expectedUsernames = [ - 'BumpyEdgeH5Y', // dydx1n88uc38xhjgxzw9nwre4ep2c8ga4fjxc575lnf - 'HappySnapWTT', // dydx1n88uc38xhjgxzw9nwre4ep2c8ga4fjxc565lnf - 'MacroMealFK5', // dydx199tqg4wdlnu4qjlxchpd7seg454937hjrknju4 + 'BubblyEarH5Y', // dydx1n88uc38xhjgxzw9nwre4ep2c8ga4fjxc575lnf + 'GreenSnowWTT', // dydx1n88uc38xhjgxzw9nwre4ep2c8ga4fjxc565lnf + 'LunarMatFK5', // dydx199tqg4wdlnu4qjlxchpd7seg454937hjrknju4 ]; expect(subaccountsWithUsernamesAfter.length).toEqual(subaccountsLength); for (let i = 0; i < expectedUsernames.length; i++) { diff --git a/indexer/services/roundtable/src/helpers/adjectives.json b/indexer/services/roundtable/src/helpers/adjectives.json index d5bace1a2b..48f7189282 100644 --- a/indexer/services/roundtable/src/helpers/adjectives.json +++ b/indexer/services/roundtable/src/helpers/adjectives.json @@ -149,10 +149,8 @@ "hip", "holy", "honest", - "hot", "huge", "humble", - "humid", "husky", "icky", "icy", @@ -190,9 +188,7 @@ "lone", "long", "loud", - "lousy", "loved", - "low", "loyal", "lucid", "lucky", @@ -234,7 +230,6 @@ "open", "other", "oval", - "pale", "paltry", "parched", "pastel", @@ -270,7 +265,6 @@ "rough", "round", "royal", - "rude", "rusty", "sacred", "sad", @@ -349,11 +343,9 @@ "umber", "unique", "unruly", - "untidy", "upbeat", "upper", "urban", - "used", "vague", "vain", "valid", @@ -378,6 +370,5 @@ "zany", "zero", "zesty", - "zippy", - "zoom" + "zippy" ] diff --git a/indexer/services/roundtable/src/helpers/nouns.json b/indexer/services/roundtable/src/helpers/nouns.json index c923cb65c5..0055867986 100644 --- a/indexer/services/roundtable/src/helpers/nouns.json +++ b/indexer/services/roundtable/src/helpers/nouns.json @@ -71,7 +71,6 @@ "bone", "book", "boot", - "born", "boss", "bow", "box", @@ -88,7 +87,6 @@ "bull", "bump", "bus", - "bush", "button", "buy", "cab", @@ -197,7 +195,6 @@ "dust", "ear", "earth", - "eat", "edge", "egg", "elbow", @@ -214,7 +211,6 @@ "ferret", "fiber", "file", - "fill", "film", "fire", "fish", @@ -226,7 +222,6 @@ "flute", "fly", "fog", - "fold", "food", "fork", "form", @@ -238,7 +233,6 @@ "fun", "game", "gap", - "gate", "gem", "gift", "giraffe", @@ -270,8 +264,6 @@ "hill", "hippo", "hit", - "hold", - "hole", "home", "honey", "hood", @@ -348,7 +340,6 @@ "melt", "meme", "menu", - "mess", "meter", "mile", "mill", @@ -466,15 +457,11 @@ "sign", "silk", "sir", - "sit", "ski", "sky", "sled", - "slip", - "slow", "smile", "smoke", - "snap", "snow", "soap", "sock", @@ -502,7 +489,6 @@ "store", "storm", "straw", - "sum", "summer", "sun", "swan", diff --git a/indexer/services/roundtable/src/tasks/subaccount-username-generator.ts b/indexer/services/roundtable/src/tasks/subaccount-username-generator.ts index 03fd93f522..44b0db8f92 100644 --- a/indexer/services/roundtable/src/tasks/subaccount-username-generator.ts +++ b/indexer/services/roundtable/src/tasks/subaccount-username-generator.ts @@ -2,6 +2,7 @@ import { logger, stats } from '@dydxprotocol-indexer/base'; import { SubaccountUsernamesTable, SubaccountsWithoutUsernamesResult, + Transaction, } from '@dydxprotocol-indexer/postgres'; import _ from 'lodash'; @@ -9,78 +10,96 @@ import config from '../config'; import { generateUsernameForSubaccount } from '../helpers/usernames-helper'; export default async function runTask(): Promise { - const start: number = Date.now(); + const taskStart: number = Date.now(); const subaccountZerosWithoutUsername: SubaccountsWithoutUsernamesResult[] = await SubaccountUsernamesTable.getSubaccountZerosWithoutUsernames( config.SUBACCOUNT_USERNAME_BATCH_SIZE, ); - let successCount: number = 0; - for (const subaccount of subaccountZerosWithoutUsername) { - for (let i = 0; i < config.ATTEMPT_PER_SUBACCOUNT; i++) { - const username: string = generateUsernameForSubaccount( - subaccount.address, - // Always use subaccountNum 0 for generation. Effectively we are - // generating one username per address. The fact that we are storing - // in the `subaccount_usernames` table is a tech debt. - 0, - // generation nonce - i, - ); - try { - await SubaccountUsernamesTable.create({ - username, - subaccountId: subaccount.subaccountId, - }); - // If success, break from loop and move to next subaccount. - successCount += 1; - break; - } catch (e) { - // There are roughly ~225 million possible usernames - // so the chance of collision is very low. - if (e instanceof Error && e.name === 'UniqueViolationError') { - stats.increment( - `${config.SERVICE_NAME}.subaccount-username-generator.collision`, 1); - logger.info({ - at: 'subaccount-username-generator#runTask', - message: 'username collision', - address: subaccount.address, - subaccountId: subaccount.subaccountId, + stats.timing( + `${config.SERVICE_NAME}.get_subaccount_zeros_without_usernames.timing`, + Date.now() - taskStart, + ); + + const txId: number = await Transaction.start(); + const txnStart: number = Date.now(); + try { + let successCount: number = 0; + for (const subaccount of subaccountZerosWithoutUsername) { + for (let i = 0; i < config.ATTEMPT_PER_SUBACCOUNT; i++) { + const username: string = generateUsernameForSubaccount( + subaccount.address, + // Always use subaccountNum 0 for generation. Effectively we are + // generating one username per address. The fact that we are storing + // in the `subaccount_usernames` table is a tech debt. + 0, + // generation nonce + i, + ); + try { + await SubaccountUsernamesTable.create({ username, - error: e, - }); - } else { - logger.error({ - at: 'subaccount-username-generator#runTask', - message: 'Failed to insert username for subaccount', - address: subaccount.address, subaccountId: subaccount.subaccountId, - username, - error: e, - }); + }, { txId }); + // If success, break from loop and move to next subaccount. + successCount += 1; + break; + } catch (e) { + // There are roughly ~225 million possible usernames + // so the chance of collision is very low. + if (e instanceof Error && e.name === 'UniqueViolationError') { + stats.increment( + `${config.SERVICE_NAME}.subaccount-username-generator.collision`, 1); + logger.info({ + at: 'subaccount-username-generator#runTask', + message: 'username collision', + address: subaccount.address, + subaccountId: subaccount.subaccountId, + username, + error: e, + }); + } else { + logger.error({ + at: 'subaccount-username-generator#runTask', + message: 'Failed to insert username for subaccount', + address: subaccount.address, + subaccountId: subaccount.subaccountId, + username, + error: e, + }); + } } } } + await Transaction.commit(txId); + const subaccountAddresses = _.map( + subaccountZerosWithoutUsername, + (subaccount) => subaccount.address, + ); + stats.timing( + `${config.SERVICE_NAME}.subaccount_username_generator.txn.timing`, + Date.now() - txnStart, + ); + logger.info({ + at: 'subaccount-username-generator#runTask', + message: 'Generated usernames', + batchSize: subaccountZerosWithoutUsername.length, + successCount, + addressSample: subaccountAddresses.slice(0, 10), + duration: Date.now() - taskStart, + }); + } catch (error) { + await Transaction.rollback(txId); + logger.error({ + at: 'subaccount-username-generator#runTask', + message: 'Error when updating totalVolume in wallets table', + error, + }); } - const subaccountAddresses = _.map( - subaccountZerosWithoutUsername, - (subaccount) => subaccount.address, - ); - - const duration = Date.now() - start; - - logger.info({ - at: 'subaccount-username-generator#runTask', - message: 'Generated usernames', - batchSize: subaccountZerosWithoutUsername.length, - successCount, - addressSample: subaccountAddresses.slice(0, 10), - duration, - }); stats.timing( - `${config.SERVICE_NAME}.subaccount_username_generator`, - duration, + `${config.SERVICE_NAME}.subaccount_username_generator.total.timing`, + Date.now() - taskStart, ); } diff --git a/indexer/services/roundtable/src/tasks/update-affiliate-info.ts b/indexer/services/roundtable/src/tasks/update-affiliate-info.ts index e3546eeec9..03fa4ef753 100644 --- a/indexer/services/roundtable/src/tasks/update-affiliate-info.ts +++ b/indexer/services/roundtable/src/tasks/update-affiliate-info.ts @@ -18,6 +18,7 @@ const defaultLastUpdateTime: string = '2024-09-16T00:00:00Z'; * Update the affiliate info for all affiliate addresses. */ export default async function runTask(): Promise { + const taskStart: number = Date.now(); // Wrap getting cache, updating info, and setting cache in one transaction so that persistent // cache and affilitate info table are in sync. const txId: number = await Transaction.start(); @@ -55,6 +56,7 @@ export default async function runTask(): Promise { at: 'update-affiliate-info#runTask', message: `Updating affiliate info from ${windowStartTime.toISO()} to ${windowEndTime.toISO()}`, }); + const updateAffiliateInfoStartTime: number = Date.now(); await AffiliateInfoTable.updateInfo(windowStartTime.toISO(), windowEndTime.toISO(), txId); await PersistentCacheTable.upsert({ key: PersistentCacheKeys.AFFILIATE_INFO_UPDATE_TIME, @@ -62,6 +64,10 @@ export default async function runTask(): Promise { }, { txId }); await Transaction.commit(txId); + stats.timing( + `${config.SERVICE_NAME}.update-affiliate-info.update-txn.timing`, + Date.now() - updateAffiliateInfoStartTime, + ); } catch (error) { await Transaction.rollback(txId); logger.error({ @@ -70,4 +76,6 @@ export default async function runTask(): Promise { error, }); } + + stats.timing(`${config.SERVICE_NAME}.update-affiliate-info.total.timing`, Date.now() - taskStart); }