towerops/lib/towerops/snmp/mempools.ex
Graham McIntire 9bb43f6007 refactor(snmp): extract metadata submodules (vlans, ips, processors, storage, mempools, sensors, interfaces, neighbors)
Splits cohesive sub-domains out of lib/towerops/snmp.ex into dedicated
submodules under lib/towerops/snmp/. Public API preserved via defdelegate
in Towerops.Snmp.

- Towerops.Snmp.Vlans, IpAddresses, Processors, StorageQueries
- Towerops.Snmp.Mempools, Sensors, Interfaces, Neighbors

Neighbors uses Towerops.Devices.DeviceQuery.for_organization/1 to remove
inline org_id filters. snmp.ex shrinks from 2742 to 2159 lines.
2026-04-30 14:26:30 -05:00

116 lines
3.1 KiB
Elixir

defmodule Towerops.Snmp.Mempools do
@moduledoc """
Read-side queries and mutations for memory pools discovered on SNMP devices,
plus persistence for mempool time-series readings.
Extracted from `Towerops.Snmp` to keep the context module focused.
"""
import Ecto.Query
alias Towerops.Repo
alias Towerops.Snmp.Mempool
alias Towerops.Snmp.MempoolReading
@doc """
Lists all memory pools for a device.
"""
def list_mempools(snmp_device_id) do
Mempool
|> where([m], m.snmp_device_id == ^snmp_device_id)
|> order_by([m], m.mempool_index)
|> Repo.all()
end
@doc """
Updates a mempool with the given attributes.
"""
def update_mempool(mempool, attrs) do
mempool
|> Mempool.changeset(attrs)
|> Repo.update()
end
@doc """
Gets recent mempool readings for a memory pool.
## Options
- `:limit` - Maximum number of readings to return (default: 100)
- `:since` - Only return readings after this datetime
"""
def get_mempool_readings(mempool_id, opts \\ []) do
limit = Keyword.get(opts, :limit, 100)
since = Keyword.get(opts, :since)
MempoolReading
|> where([r], r.mempool_id == ^mempool_id)
|> order_by([r], desc: r.checked_at)
|> limit(^limit)
|> maybe_filter_since(since)
|> Repo.all()
end
defp maybe_filter_since(query, nil), do: query
defp maybe_filter_since(query, since), do: where(query, [r], r.checked_at >= ^since)
@doc """
Gets the latest mempool reading for a memory pool.
"""
def get_latest_mempool_reading(mempool_id) do
MempoolReading
|> where([r], r.mempool_id == ^mempool_id)
|> order_by([r], desc: r.checked_at)
|> limit(1)
|> Repo.one()
end
@doc """
Gets the latest mempool readings for multiple memory pools in a single query.
Returns a map of %{mempool_id => reading} for efficient batch loading.
Mempools without readings will not be present in the map.
"""
def get_latest_mempool_readings_batch([]), do: %{}
def get_latest_mempool_readings_batch(mempool_ids) when is_list(mempool_ids) do
from(r in MempoolReading,
where: r.mempool_id in ^mempool_ids,
distinct: r.mempool_id,
order_by: [asc: r.mempool_id, desc: r.checked_at]
)
|> Repo.all()
|> Map.new(fn reading -> {reading.mempool_id, reading} end)
end
@doc """
Records a new mempool reading.
"""
def create_mempool_reading(attrs) do
%MempoolReading{}
|> MempoolReading.changeset(attrs)
|> Repo.insert()
end
@doc """
Batch inserts multiple mempool readings using `Repo.insert_all/3`.
Accepts a list of attribute maps with the same keys as `create_mempool_reading/1`.
Generates UUIDs and timestamps automatically. Returns `{count, nil}`.
"""
@spec create_mempool_readings_batch([map()]) :: {non_neg_integer(), nil}
def create_mempool_readings_batch([]), do: {0, nil}
def create_mempool_readings_batch(entries) when is_list(entries) do
now = Towerops.Time.now()
rows =
Enum.map(entries, fn entry ->
entry
|> Map.put(:id, Ecto.UUID.generate())
|> Map.put(:inserted_at, now)
end)
Repo.insert_all(MempoolReading, rows)
end
end