Optimize list_agent_polling_targets to use SQL filtering
Rewrote list_agent_polling_targets/1 to filter equipment using SQL WHERE clause instead of loading all SNMP-enabled equipment into memory and filtering in Elixir. Previous implementation: - Loaded ALL SNMP-enabled equipment with preloads (potentially 10,000+ records) - Filtered in application code with equipment_matches_agent?/2 helper - Called get_equipment_assignment/1 for each piece of equipment (N+1 queries) - Memory-intensive and slow at scale New implementation: - Single SQL query with LEFT JOIN and compound WHERE clause - Evaluates hierarchical assignment (equipment → site → organization) in SQL - Only loads equipment assigned to the specific agent - Scales efficiently to 10,000+ equipment Performance improvement: - With 10,000 equipment, 1000 assigned to agent: - Before: Load 10,000 records + 10,000 queries = massive memory + slow - After: Load 1,000 records in 1 query = 10x memory reduction + instant Removed unused helper functions: - equipment_matches_agent?/2 - site_matches_agent?/2 All 40 agent tests passing.
This commit is contained in:
parent
723f064315
commit
bb5766f1a6
1 changed files with 14 additions and 42 deletions
|
|
@ -239,55 +239,27 @@ defmodule Towerops.Agents do
|
|||
|
||||
"""
|
||||
def list_agent_polling_targets(agent_token_id) do
|
||||
# Get all equipment with necessary preloads
|
||||
equipment_query =
|
||||
# Use SQL to filter equipment by effective agent assignment
|
||||
# This avoids loading all equipment into memory and filtering in application code
|
||||
Repo.all(
|
||||
from(e in Equipment,
|
||||
where: e.snmp_enabled == true,
|
||||
join: s in assoc(e, :site),
|
||||
join: o in assoc(s, :organization),
|
||||
left_join: aa in AgentAssignment,
|
||||
on: aa.equipment_id == e.id and aa.enabled == true,
|
||||
where:
|
||||
e.snmp_enabled == true and
|
||||
(aa.agent_token_id == ^agent_token_id or
|
||||
(is_nil(aa.agent_token_id) and s.agent_token_id == ^agent_token_id) or
|
||||
(is_nil(aa.agent_token_id) and is_nil(s.agent_token_id) and
|
||||
o.default_agent_token_id == ^agent_token_id)),
|
||||
preload: [
|
||||
:agent_assignments,
|
||||
site: :organization,
|
||||
snmp_device: [:sensors, :interfaces]
|
||||
]
|
||||
)
|
||||
|
||||
equipment_list = Repo.all(equipment_query)
|
||||
|
||||
# Filter to only equipment that resolves to this agent
|
||||
Enum.filter(equipment_list, fn equipment ->
|
||||
equipment_matches_agent?(equipment, agent_token_id)
|
||||
end)
|
||||
end
|
||||
|
||||
defp equipment_matches_agent?(equipment, agent_token_id) do
|
||||
# Check direct assignment first
|
||||
case get_equipment_assignment(equipment.id) do
|
||||
%AgentAssignment{agent_token_id: ^agent_token_id, enabled: true} ->
|
||||
true
|
||||
|
||||
%AgentAssignment{} ->
|
||||
# Has a different direct assignment
|
||||
false
|
||||
|
||||
nil ->
|
||||
# No direct assignment, check site then org
|
||||
site = equipment.site
|
||||
site_matches_agent?(site, agent_token_id)
|
||||
end
|
||||
end
|
||||
|
||||
defp site_matches_agent?(site, agent_token_id) do
|
||||
cond do
|
||||
# Check site agent
|
||||
site.agent_token_id == agent_token_id ->
|
||||
true
|
||||
|
||||
# Check org default (only if site doesn't have an agent set)
|
||||
is_nil(site.agent_token_id) and site.organization.default_agent_token_id == agent_token_id ->
|
||||
true
|
||||
|
||||
true ->
|
||||
false
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
@doc """
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue