Refacto load all functions
Unbounded LoadAll* loaders materialised an entire result set in one query with no ceiling. A table that is small in development can grow without bound in production, so these loaders were a latent memory and query-time hazard. Remove the LoadAll* methods from pkg/coredata and walk the cursor- paginated LoadBy* siblings instead through a shared page.LoadAll helper. The helper advances a MaxCursorSize forward cursor until the result set is exhausted and concatenates the pages. It caps a single call at MaxLoadAllPages (20) batches of 500 rows and errors past that rather than materialising an unbounded set, so a runaway caller fails loudly instead of exhausting memory. Callers that genuinely need every row now express that explicitly, and the coredata load-naming rule and docs are updated to discourage new unbounded loaders. Signed-off-by: Sacha Al Himdani <sacha@probo.com>
This commit is contained in:
committed by
Sacha Al Himdani
parent
853f2404a6
commit
9ab8ea2085
@@ -566,118 +566,6 @@ WHERE
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *MembershipProfiles) LoadAllByOrganizationID(
|
||||
ctx context.Context,
|
||||
conn pg.Querier,
|
||||
scope Scoper,
|
||||
organizationID gid.GID,
|
||||
filter *MembershipProfileFilter,
|
||||
) error {
|
||||
q := `
|
||||
WITH profiles AS (
|
||||
SELECT
|
||||
p.id,
|
||||
p.identity_id,
|
||||
p.organization_id,
|
||||
i.email_address,
|
||||
p.source,
|
||||
p.state,
|
||||
p.full_name,
|
||||
p.kind,
|
||||
p.additional_email_addresses,
|
||||
p.position,
|
||||
p.contract_start_date,
|
||||
p.contract_end_date,
|
||||
p.user_name,
|
||||
p.external_id,
|
||||
p.nickname,
|
||||
p.locale,
|
||||
p.timezone,
|
||||
p.profile_url,
|
||||
p.preferred_language,
|
||||
p.given_name,
|
||||
p.family_name,
|
||||
p.formatted_name,
|
||||
p.middle_name,
|
||||
p.honorific_prefix,
|
||||
p.honorific_suffix,
|
||||
p.employee_number,
|
||||
p.department,
|
||||
p.cost_center,
|
||||
p.enterprise_organization,
|
||||
p.division,
|
||||
p.manager_value,
|
||||
p.created_at,
|
||||
p.updated_at
|
||||
FROM
|
||||
iam_membership_profiles p
|
||||
INNER JOIN identities i ON i.id = p.identity_id
|
||||
WHERE
|
||||
p.%s
|
||||
AND p.organization_id = @organization_id
|
||||
AND %s
|
||||
)
|
||||
SELECT
|
||||
id,
|
||||
identity_id,
|
||||
organization_id,
|
||||
email_address,
|
||||
source,
|
||||
state,
|
||||
full_name,
|
||||
kind,
|
||||
additional_email_addresses,
|
||||
position,
|
||||
contract_start_date,
|
||||
contract_end_date,
|
||||
'' AS organization_name,
|
||||
user_name,
|
||||
external_id,
|
||||
nickname,
|
||||
locale,
|
||||
timezone,
|
||||
profile_url,
|
||||
preferred_language,
|
||||
given_name,
|
||||
family_name,
|
||||
formatted_name,
|
||||
middle_name,
|
||||
honorific_prefix,
|
||||
honorific_suffix,
|
||||
employee_number,
|
||||
department,
|
||||
cost_center,
|
||||
enterprise_organization,
|
||||
division,
|
||||
manager_value,
|
||||
created_at,
|
||||
updated_at
|
||||
FROM profiles
|
||||
`
|
||||
|
||||
q = fmt.Sprintf(q, scope.SQLFragment(), filter.SQLFragment())
|
||||
|
||||
args := pgx.NamedArgs{
|
||||
"organization_id": organizationID,
|
||||
}
|
||||
maps.Copy(args, scope.SQLArguments())
|
||||
maps.Copy(args, filter.SQLArguments())
|
||||
|
||||
rows, err := conn.Query(ctx, q, args)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot query profiles: %w", err)
|
||||
}
|
||||
|
||||
profiles, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[MembershipProfile])
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot collect profiles: %w", err)
|
||||
}
|
||||
|
||||
*p = profiles
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *MembershipProfiles) LoadByIdentityID(
|
||||
ctx context.Context,
|
||||
conn pg.Querier,
|
||||
|
||||
Reference in New Issue
Block a user