As funções rodavam no default iad1 (Virgínia) enquanto o Neon fica em sa-east-1 (São Paulo): cada query do Payload atravessava o equador duas vezes (~120 ms por ida-e-volta) e uma página do /campanha faz uma dúzia. Medido em produção: 410-470 ms de TTFB para um 307 que não toca o banco. - vercel.json fixa regions: [gru1], mesma região AWS do banco (Hobby permite uma região, mas permite escolher qual). - loadStatewideSuggestedGoals passa a ser exportada e é disparada dentro do Promise.all que já lê o escopo, na lista e no dashboard: o read do global campaignGoals saía sozinho no fim da requisição. - loadMunicipalityScope lê os pledges em paralelo quando o ator é irrestrito e não há filtro (aí o IN de 435 ids não exclui nada); qualquer escopo mais estreito continua esperando os ids. Débitos medidos no caminho e registrados em TECH-DEBT: o fan-out de leituras do ator em getFreshCampaignUser (~5 por render) e a leitura dupla em authenticateCampaignToken.
100 lines
3 KiB
TypeScript
100 lines
3 KiB
TypeScript
import 'server-only'
|
|
|
|
import { cache } from 'react'
|
|
|
|
import type { Payload, Where } from 'payload'
|
|
|
|
import { isUnrestrictedCampaignRole } from '@/lib/campaignRoles'
|
|
import type { CampaignUser, Municipality } from '@/payload-types'
|
|
import {
|
|
aggregateAllPledgesByMunicipality,
|
|
aggregatePledgesByMunicipality,
|
|
} from '@/utilities/votePledgeData'
|
|
import { type MunicipalityPledgeAggregate } from '@/utilities/votePledgeViews'
|
|
|
|
/** Superset of the fields the dashboard, map, and list overview consume. */
|
|
type ScopedMunicipalityDoc = Pick<
|
|
Municipality,
|
|
| 'id'
|
|
| 'name'
|
|
| 'slug'
|
|
| 'kind'
|
|
| 'region'
|
|
| 'ibgeCode'
|
|
| 'advisors'
|
|
| 'priority'
|
|
| 'expectedVotes'
|
|
>
|
|
|
|
export type MunicipalityScope = {
|
|
municipalities: ScopedMunicipalityDoc[]
|
|
pledgeAggregates: Map<number, MunicipalityPledgeAggregate>
|
|
}
|
|
|
|
const loadScope = async (
|
|
payload: Payload,
|
|
user: CampaignUser,
|
|
where: Where,
|
|
): Promise<MunicipalityScope> => {
|
|
const municipalitiesPromise = payload
|
|
.find({
|
|
collection: 'municipality',
|
|
depth: 0,
|
|
limit: 0,
|
|
pagination: false,
|
|
select: {
|
|
name: true,
|
|
slug: true,
|
|
kind: true,
|
|
region: true,
|
|
ibgeCode: true,
|
|
advisors: true,
|
|
priority: true,
|
|
expectedVotes: true,
|
|
},
|
|
where,
|
|
user,
|
|
overrideAccess: false,
|
|
})
|
|
.then((result) => result.docs as ScopedMunicipalityDoc[])
|
|
|
|
// An unrestricted actor with no filter has the WHOLE catalog in scope, so the
|
|
// pledge aggregate has nothing to narrow: both reads can leave in the same
|
|
// round trip. Every narrower scope still has to wait for the município ids —
|
|
// the aggregate would otherwise count pledges the actor cannot read.
|
|
if (isUnrestrictedCampaignRole(user.role) && Object.keys(where).length === 0) {
|
|
const [municipalities, pledgeAggregates] = await Promise.all([
|
|
municipalitiesPromise,
|
|
aggregateAllPledgesByMunicipality(payload),
|
|
])
|
|
|
|
return { municipalities, pledgeAggregates }
|
|
}
|
|
|
|
const municipalities = await municipalitiesPromise
|
|
const pledgeAggregates = await aggregatePledgesByMunicipality(
|
|
payload,
|
|
municipalities.map((municipality) => municipality.id),
|
|
)
|
|
|
|
return { municipalities, pledgeAggregates }
|
|
}
|
|
|
|
/**
|
|
* Request-scoped municipality scope (accessible docs + pledge aggregates),
|
|
* deduplicated with React `cache()`: the staff dashboard and the map bundle
|
|
* both need the same scope on one request and used to load it twice.
|
|
*
|
|
* `payload` (module singleton) and `user` (from the request-cached
|
|
* `getCampaignUser`) are reference-stable within a request; the `where` clause
|
|
* participates via its canonical JSON string.
|
|
*/
|
|
const loadScopeCached = cache((payload: Payload, user: CampaignUser, whereKey: string) =>
|
|
loadScope(payload, user, JSON.parse(whereKey) as Where),
|
|
)
|
|
|
|
export const loadMunicipalityScope = (
|
|
payload: Payload,
|
|
user: CampaignUser,
|
|
where: Where,
|
|
): Promise<MunicipalityScope> => loadScopeCached(payload, user, JSON.stringify(where))
|