Something went wrong. Try again.
Mirrored from GitHub github.com/roostorg/coop
Something went wrong. Try again.
3.2 kB · 91 lines
TypeScript
at main
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192import { type ItemIdentifier } from '@roostorg/coop-types';import { type Kysely } from 'kysely';import _ from 'lodash';import { type ReadonlyDeep } from 'type-fest';
import { inject, type Dependencies } from '../../iocContainer/index.js';import { type UserStatisticsServiceWarehouse } from './dbTypes.js';
export type UserActionStatistics = { userId: string; userTypeId: string; orgId: string; actionId: string; policyId: string | null; actorId: string | null; itemSubmissionIds: string[]; count: number;};
const { chunk, uniqBy } = _;
/** * This is an internal function for querying a batch of users' lifetime action * statistics from the data warehouse. It's not called directly by consumers of the user * statistics service; instead, it's used by the service internally to freshen * the cache that serves consumers' requests. * * @internal */export const makeFetchUserActionStatistics = inject( ['DataWarehouseDialect'], (dialect: Dependencies['DataWarehouseDialect']) => { const warehouseKysely = dialect.getKyselyInstance() as Kysely<UserStatisticsServiceWarehouse>; return async ( opts: ReadonlyDeep<{ orgId: string; userItemIdentifiers: ItemIdentifier[]; }>, ): Promise<UserActionStatistics[]> => { const { orgId, userItemIdentifiers } = opts;
// The warehouse has a published limit of ~16,000 entries in a list expression // (like we use in our `USER_ID IN (...)` filter), so we have to chunk the // user ids. The warehouse also has a published limit of 1MB for the total // length of the query (which must also coopr the size bind parameter // values). However, we were getting warehouse errors in practice well // below these limits, for not-totally-clear reasons. So, we set the limit // here to the biggest chunk size that worked reliably. const uniqUserItemIdentifiers = uniqBy( userItemIdentifiers, (it) => `${it.id}:${it.typeId}`, ); const userItemIdentifierBatches = chunk(uniqUserItemIdentifiers, 1000);
const makeQueryForBatch = (userItemIdentifiers: ItemIdentifier[]) => warehouseKysely .selectFrom('USER_STATISTICS_SERVICE.LIFETIME_ACTION_STATS') .select([ 'USER_ID as userId', 'USER_TYPE_ID as userTypeId', 'ACTION_ID as actionId', 'POLICY_ID as policyId', 'ITEM_SUBMISSION_IDS as itemSubmissionIds', 'ACTOR_ID as actorId', 'COUNT as count', ]) .where('ORG_ID', '=', orgId) .where(({ eb, and, or }) => or( userItemIdentifiers.map((itemIdentifier) => and([ eb('USER_ID', '=', itemIdentifier.id), eb('USER_TYPE_ID', '=', itemIdentifier.typeId), ]), ), ), );
const results = await Promise.all( userItemIdentifierBatches.map(async (it) => makeQueryForBatch(it) .execute() .then((results) => results.map((it) => ({ ...it, orgId }))), ), );
return results.flat(); }; },);