fix usersByCountry query (#1003)

<!--

Make sure you've read the CONTRIBUTING.md guidelines:
https://github.com/stack-auth/stack-auth/blob/dev/CONTRIBUTING.md

-->


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Improvements**
* Optimized country-level metrics calculation with intelligent sampling
and automatic scaling to project accurate user counts from sample data.
* Enhanced data aggregation logic with improved handling of edge cases
and invalid entries.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
BilalG1 2025-11-06 11:19:09 -08:00 committed by GitHub
parent 5c816a02e3
commit 793de9e3a0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -14,6 +14,7 @@ type DataPoints = yup.InferType<typeof DataPointsSchema>;
const METRICS_CACHE_NAMESPACE = "metrics";
const ONE_HOUR_MS = 60 * 60 * 1000;
const MAX_USERS_FOR_COUNTRY_SAMPLE = 10_000;
async function withMetricsCache<T>(tenancy: Tenancy, suffix: string, prisma: PrismaClientTransaction, includeAnonymous: boolean = false, loader: () => Promise<T>): Promise<T> {
return await getOrSetCacheValue<T>({
@ -32,12 +33,20 @@ const DataPointsSchema = yupArray(yupObject({
async function loadUsersByCountry(tenancy: Tenancy, prisma: PrismaClientTransaction, includeAnonymous: boolean = false): Promise<Record<string, number>> {
const totalUsers = await prisma.projectUser.count({
where: {
tenancyId: tenancy.id,
...(includeAnonymous ? {} : { isAnonymous: false }),
},
});
const users = await prisma.projectUser.findMany({
where: {
tenancyId: tenancy.id,
...(includeAnonymous ? {} : { isAnonymous: false }),
},
select: { projectUserId: true },
orderBy: { projectUserId: "asc" },
take: Math.min(totalUsers, MAX_USERS_FOR_COUNTRY_SAMPLE),
});
if (users.length === 0) {
@ -46,6 +55,7 @@ async function loadUsersByCountry(tenancy: Tenancy, prisma: PrismaClientTransact
const userIds = users.map((user) => user.projectUserId);
const userIdArray = Prisma.sql`ARRAY[${Prisma.join(userIds.map((id) => Prisma.sql`${id}`))}]::text[]`;
const scalingFactor = totalUsers > users.length ? totalUsers / users.length : 1;
const rows = await globalPrismaClient.$queryRaw<{ countryCode: string | null, userCount: bigint }[]>(Prisma.sql`
WITH latest_ip AS (
@ -70,8 +80,15 @@ async function loadUsersByCountry(tenancy: Tenancy, prisma: PrismaClientTransact
`);
return Object.fromEntries(
rows.map(({ userCount, countryCode }) => [countryCode, Number(userCount)])
.filter(([countryCode]) => countryCode)
rows.map(({ userCount, countryCode }) => {
if (!countryCode) {
return null;
}
const count = Number(userCount);
const estimatedCount = scalingFactor === 1 ? count : Math.round(count * scalingFactor);
return [countryCode, estimatedCount] as [string, number];
})
.filter((entry): entry is [string, number] => entry !== null)
);
}