- Fix SQL injection in partition_manager, db_optimizer, and release.ex - Fix XSS vulnerabilities with proper HTML escaping in LiveViews - Add proper error handling for email delivery functions - Fix race conditions with advisory locks and atomic operations - Replace unsupervised spawn/Task.start with supervised alternatives - Convert ETS operations to GenServer serialization for thread safety - Change ETS tables from :public to :protected access - Add client limits to prevent unbounded memory growth - Add PubSub cleanup in GenServer terminate callbacks - Fix device upsert to use atomic Repo.insert_all
71 lines
1.7 KiB
Elixir
71 lines
1.7 KiB
Elixir
defmodule Aprsme.RegexCache do
|
|
@moduledoc """
|
|
A simple in-memory cache for compiled regex patterns to avoid recompilation.
|
|
Uses ETS for thread-safe access.
|
|
"""
|
|
|
|
use GenServer
|
|
|
|
require Logger
|
|
|
|
@table_name :regex_cache
|
|
@max_cache_size 1000
|
|
|
|
def start_link(_opts) do
|
|
GenServer.start_link(__MODULE__, %{}, name: __MODULE__)
|
|
end
|
|
|
|
@impl true
|
|
def init(_) do
|
|
# Create ETS table for caching compiled regexes
|
|
:ets.new(@table_name, [:set, :public, :named_table, read_concurrency: true])
|
|
{:ok, %{}}
|
|
end
|
|
|
|
@doc """
|
|
Get or compile a regex pattern. Returns {:ok, regex} or {:error, reason}.
|
|
"""
|
|
def get_or_compile(pattern_string) do
|
|
GenServer.call(__MODULE__, {:get_or_compile, pattern_string})
|
|
end
|
|
|
|
@impl true
|
|
def handle_call({:get_or_compile, pattern_string}, _from, state) do
|
|
result =
|
|
case :ets.lookup(@table_name, pattern_string) do
|
|
[{^pattern_string, regex}] ->
|
|
{:ok, regex}
|
|
|
|
[] ->
|
|
compile_and_cache(pattern_string)
|
|
end
|
|
|
|
{:reply, result, state}
|
|
end
|
|
|
|
defp compile_and_cache(pattern_string) do
|
|
case Regex.compile(pattern_string) do
|
|
{:ok, regex} ->
|
|
if :ets.info(@table_name, :size) >= @max_cache_size do
|
|
clear_oldest_entries()
|
|
end
|
|
|
|
:ets.insert(@table_name, {pattern_string, regex})
|
|
{:ok, regex}
|
|
|
|
error ->
|
|
error
|
|
end
|
|
end
|
|
|
|
defp clear_oldest_entries do
|
|
# Simple strategy: clear half the cache
|
|
# In production, you might want LRU eviction
|
|
entries = :ets.tab2list(@table_name)
|
|
to_remove = Enum.take(entries, div(length(entries), 2))
|
|
|
|
Enum.each(to_remove, fn {key, _} ->
|
|
:ets.delete(@table_name, key)
|
|
end)
|
|
end
|
|
end
|