Packages
fnord
0.8.45
0.9.40
0.9.39
0.9.38
0.9.37
0.9.36
0.9.35
0.9.34
0.9.33
0.9.32
0.9.31
0.9.30
0.9.29
0.9.28
0.9.27
0.9.26
0.9.25
0.9.24
0.9.23
0.9.22
0.9.21
0.9.20
0.9.19
0.9.18
0.9.17
0.9.16
0.9.15
0.9.14
0.9.13
0.9.12
0.9.11
0.9.10
0.9.9
0.9.8
0.9.7
0.9.6
0.9.5
0.9.4
0.9.3
0.9.2
0.9.1
0.9.0
0.8.99
0.8.98
0.8.97
0.8.96
0.8.95
0.8.94
0.8.93
0.8.92
0.8.91
0.8.90
0.8.89
0.8.88
0.8.87
0.8.86
0.8.85
0.8.84
0.8.83
0.8.82
0.8.81
0.8.80
0.8.79
0.8.78
0.8.77
0.8.76
0.8.75
0.8.74
0.8.73
0.8.72
0.8.71
0.8.70
0.8.69
0.8.68
0.8.67
0.8.66
0.8.65
0.8.64
0.8.63
0.8.62
0.8.61
0.8.60
0.8.59
0.8.58
0.8.57
0.8.56
0.8.55
0.8.54
0.8.53
0.8.52
0.8.51
0.8.50
0.8.49
0.8.48
0.8.47
0.8.46
0.8.45
0.8.44
0.8.43
0.8.42
0.8.41
0.8.40
0.8.39
0.8.38
0.8.37
0.8.36
0.8.35
0.8.34
0.8.33
0.8.32
0.8.31
0.8.30
0.8.29
0.8.27
0.8.26
0.8.25
0.8.24
0.8.23
0.8.22
0.8.21
0.8.20
0.8.19
0.8.18
0.8.17
0.8.16
0.8.15
0.8.14
0.8.13
0.8.12
0.8.11
0.8.1
0.8.0
0.7.24
0.7.23
0.7.22
0.7.21
0.7.20
0.7.19
0.7.18
0.7.17
0.7.16
0.7.15
0.7.14
0.7.13
0.7.12
0.7.11
0.7.10
0.7.9
0.7.8
0.7.7
0.7.6
0.7.5
0.7.3
0.7.2
0.7.1
0.7.0
0.6.9
0.6.8
0.6.7
0.6.6
0.6.5
0.6.4
0.6.3
0.6.1
0.6.0
0.5.9
0.5.8
0.5.7
0.5.6
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.44
0.4.43
0.4.42
0.4.41
0.4.40
0.4.39
0.4.38
0.4.37
0.4.36
0.4.35
0.4.34
0.4.33
0.4.32
0.4.30
0.4.29
0.4.28
0.4.27
0.4.26
0.4.25
0.4.24
0.4.23
0.4.22
0.4.21
0.4.20
0.4.19
0.4.18
0.4.17
0.4.16
0.4.15
0.4.14
0.4.13
0.4.12
0.4.11
0.4.10
0.4.9
0.4.8
0.4.7
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.0
0.2.0
0.1.0
AI code archaeology
Current section
Files
Jump to
Current section
Files
lib/settings/instrumentation.ex
defmodule Settings.Instrumentation do
@moduledoc """
Capture and guard the approvals section on every write.
"""
require Logger
@baseline_table :fnord_approvals_baseline
@trace_table :fnord_approvals_traces
@spec init_baseline(map) :: :ok
def init_baseline(data) do
initial = Map.get(data, "approvals", %{})
Application.put_env(:fnord, :initial_global_approvals_snapshot, initial)
if :ets.whereis(@baseline_table) == :undefined do
:ets.new(@baseline_table, [:named_table, :public, read_concurrency: true])
else
:ets.delete_all_objects(@baseline_table)
end
:ets.insert(@baseline_table, {:baseline, initial})
if :ets.whereis(@trace_table) == :undefined do
:ets.new(@trace_table, [:named_table, :public, :bag, read_concurrency: true])
end
:ok
end
@spec record_trace(atom, any, map, map) :: :ok
def record_trace(op, key, before, after_value) do
# Ensure ETS table exists before inserting the trace
case :ets.whereis(@trace_table) do
:undefined -> init_baseline(before)
_ -> :ok
end
ts = System.system_time(:millisecond)
# compute counts for before and after, ensuring after_counts has zeros for cleared keys
before_counts = kind_counts(before)
raw_after = kind_counts(after_value)
after_counts =
before_counts
|> Enum.map(fn {k, _v} -> {k, Map.get(raw_after, k, 0)} end)
|> Enum.into(%{})
# build entry map for insertion
entry = %{
ts: ts,
pid: self(),
op: op,
key: key,
before_counts: before_counts,
after_counts: after_counts,
stack: trimmed_stack(20)
}
# Insert trace entry; ETS bag allows multiple entries per key
:ets.insert(@trace_table, {:trace, ts, entry})
:ok
end
@spec guard_or_heal(map, map, map) :: map
def guard_or_heal(before, after_value, %{op: op, key: key}) do
bc = kind_counts(before)
ac = kind_counts(after_value)
baseline =
case :ets.lookup(@baseline_table, :baseline) do
[{:baseline, b}] -> b
_ -> %{}
end
if approvals_cleared?(before, after_value) and Enum.any?(bc, fn {_k, v} -> v > 0 end) do
if debug?() do
dump_debug(op, key, baseline, bc, ac)
after_value
else
healed = merge_approvals(baseline, Map.get(after_value, "approvals", %{}))
:ets.insert(@baseline_table, {:baseline, healed})
Map.put(after_value, "approvals", healed)
end
else
after_value
end
end
@spec recent_traces(pos_integer) :: [map]
def recent_traces(n) do
:ets.lookup(@trace_table, :trace)
|> Enum.map(fn {:trace, _ts, e} -> e end)
|> Enum.sort_by(& &1.ts, :desc)
|> Enum.take(n)
end
defp kind_counts(data) do
case Map.get(data, "approvals") do
ap when is_map(ap) ->
ap
|> Enum.into(%{}, fn {k, v} -> {k, if(is_list(v), do: length(v), else: 0)} end)
_ ->
%{}
end
end
defp approvals_cleared?(_before, after_data) do
Map.get(after_data, "approvals", %{})
|> Enum.all?(fn {_k, v} -> v == [] end)
end
defp merge_approvals(baseline, curr) do
Map.merge(baseline, curr, fn _k, o, n ->
(o ++
n)
|> Enum.uniq()
|> Enum.sort()
end)
end
@doc """
Returns true if the FNORD_DEBUG_SETTINGS environment variable is set to
"1", "true", or "yes" (case-insensitive).
"""
def debug?() do
case System.get_env("FNORD_DEBUG_SETTINGS")
|> to_string()
|> String.downcase() do
v when v in ["1", "true", "yes"] -> true
_ -> false
end
end
defp dump_debug(op, key, baseline, bc, ac) do
# Log warning about cleared approvals in debug mode
Logger.warning(
"Settings FNORD_DEBUG_SETTINGS: approvals would have been cleared by #{op} on #{inspect(key)}.\n baseline=#{inspect(baseline)} before=#{inspect(bc)} after=#{inspect(ac)}"
)
{:current_stacktrace, st} = Process.info(self(), :current_stacktrace)
Logger.error("stack:\n#{Exception.format_stacktrace(st)}")
Logger.error("traces:\n#{inspect(recent_traces(10), limit: :infinity)}")
end
defp trimmed_stack(limit) do
{:current_stacktrace, st} = Process.info(self(), :current_stacktrace)
st
|> Enum.drop(2)
|> Enum.take(limit)
end
end