|
1 |
| -defmodule Sentry.OpenTelemetry.SpanStorage do |
2 |
| - @moduledoc false |
3 |
| - use GenServer |
| 1 | +if Sentry.OpenTelemetry.VersionChecker.tracing_compatible?() do |
| 2 | + defmodule Sentry.OpenTelemetry.SpanStorage do |
| 3 | + @moduledoc false |
| 4 | + use GenServer |
4 | 5 |
|
5 |
| - defstruct [:cleanup_interval, :table_name] |
| 6 | + defstruct [:cleanup_interval, :table_name] |
6 | 7 |
|
7 |
| - alias Sentry.OpenTelemetry.SpanRecord |
| 8 | + alias Sentry.OpenTelemetry.SpanRecord |
8 | 9 |
|
9 |
| - @cleanup_interval :timer.minutes(5) |
| 10 | + @cleanup_interval :timer.minutes(5) |
10 | 11 |
|
11 |
| - @span_ttl 30 * 60 |
| 12 | + @span_ttl 30 * 60 |
12 | 13 |
|
13 |
| - @spec start_link(keyword()) :: GenServer.on_start() |
14 |
| - def start_link(opts) when is_list(opts) do |
15 |
| - name = Keyword.get(opts, :name, __MODULE__) |
16 |
| - GenServer.start_link(__MODULE__, opts, name: name) |
17 |
| - end |
| 14 | + @spec start_link(keyword()) :: GenServer.on_start() |
| 15 | + def start_link(opts) when is_list(opts) do |
| 16 | + name = Keyword.get(opts, :name, __MODULE__) |
| 17 | + GenServer.start_link(__MODULE__, opts, name: name) |
| 18 | + end |
18 | 19 |
|
19 |
| - @impl true |
20 |
| - def init(opts) do |
21 |
| - table_name = Keyword.get(opts, :table_name, default_table_name()) |
22 |
| - cleanup_interval = Keyword.get(opts, :cleanup_interval, @cleanup_interval) |
| 20 | + @impl true |
| 21 | + def init(opts) do |
| 22 | + table_name = Keyword.get(opts, :table_name, default_table_name()) |
| 23 | + cleanup_interval = Keyword.get(opts, :cleanup_interval, @cleanup_interval) |
23 | 24 |
|
24 |
| - _ = :ets.new(table_name, [:named_table, :public, :ordered_set]) |
| 25 | + _ = :ets.new(table_name, [:named_table, :public, :ordered_set]) |
25 | 26 |
|
26 |
| - schedule_cleanup(cleanup_interval) |
| 27 | + schedule_cleanup(cleanup_interval) |
27 | 28 |
|
28 |
| - {:ok, %__MODULE__{cleanup_interval: cleanup_interval, table_name: table_name}} |
29 |
| - end |
| 29 | + {:ok, %__MODULE__{cleanup_interval: cleanup_interval, table_name: table_name}} |
| 30 | + end |
30 | 31 |
|
31 |
| - @impl true |
32 |
| - def handle_info(:cleanup_stale_spans, state) do |
33 |
| - cleanup_stale_spans(state.table_name) |
34 |
| - schedule_cleanup(state.cleanup_interval) |
| 32 | + @impl true |
| 33 | + def handle_info(:cleanup_stale_spans, state) do |
| 34 | + cleanup_stale_spans(state.table_name) |
| 35 | + schedule_cleanup(state.cleanup_interval) |
35 | 36 |
|
36 |
| - {:noreply, state} |
37 |
| - end |
| 37 | + {:noreply, state} |
| 38 | + end |
38 | 39 |
|
39 |
| - @spec store_span(SpanRecord.t(), keyword()) :: true |
40 |
| - def store_span(span_data, opts \\ []) do |
41 |
| - table_name = Keyword.get(opts, :table_name, default_table_name()) |
42 |
| - stored_at = System.system_time(:second) |
| 40 | + @spec store_span(SpanRecord.t(), keyword()) :: true |
| 41 | + def store_span(span_data, opts \\ []) do |
| 42 | + table_name = Keyword.get(opts, :table_name, default_table_name()) |
| 43 | + stored_at = System.system_time(:second) |
43 | 44 |
|
44 |
| - if span_data.parent_span_id == nil do |
45 |
| - :ets.insert(table_name, {{:root_span, span_data.span_id}, span_data, stored_at}) |
46 |
| - else |
47 |
| - key = {:child_span, span_data.parent_span_id, span_data.span_id} |
| 45 | + if span_data.parent_span_id == nil do |
| 46 | + :ets.insert(table_name, {{:root_span, span_data.span_id}, span_data, stored_at}) |
| 47 | + else |
| 48 | + key = {:child_span, span_data.parent_span_id, span_data.span_id} |
48 | 49 |
|
49 |
| - :ets.insert(table_name, {key, span_data, stored_at}) |
| 50 | + :ets.insert(table_name, {key, span_data, stored_at}) |
| 51 | + end |
50 | 52 | end
|
51 |
| - end |
52 | 53 |
|
53 |
| - @spec get_root_span(String.t(), keyword()) :: SpanRecord.t() | nil |
54 |
| - def get_root_span(span_id, opts \\ []) do |
55 |
| - table_name = Keyword.get(opts, :table_name, default_table_name()) |
| 54 | + @spec get_root_span(String.t(), keyword()) :: SpanRecord.t() | nil |
| 55 | + def get_root_span(span_id, opts \\ []) do |
| 56 | + table_name = Keyword.get(opts, :table_name, default_table_name()) |
56 | 57 |
|
57 |
| - case :ets.lookup(table_name, {:root_span, span_id}) do |
58 |
| - [{{:root_span, ^span_id}, span, _stored_at}] -> span |
59 |
| - [] -> nil |
| 58 | + case :ets.lookup(table_name, {:root_span, span_id}) do |
| 59 | + [{{:root_span, ^span_id}, span, _stored_at}] -> span |
| 60 | + [] -> nil |
| 61 | + end |
60 | 62 | end
|
61 |
| - end |
62 | 63 |
|
63 |
| - @spec get_child_spans(String.t(), keyword()) :: [SpanRecord.t()] |
64 |
| - def get_child_spans(parent_span_id, opts \\ []) do |
65 |
| - table_name = Keyword.get(opts, :table_name, default_table_name()) |
| 64 | + @spec get_child_spans(String.t(), keyword()) :: [SpanRecord.t()] |
| 65 | + def get_child_spans(parent_span_id, opts \\ []) do |
| 66 | + table_name = Keyword.get(opts, :table_name, default_table_name()) |
66 | 67 |
|
67 |
| - get_all_descendants(parent_span_id, table_name) |
68 |
| - end |
| 68 | + get_all_descendants(parent_span_id, table_name) |
| 69 | + end |
69 | 70 |
|
70 |
| - defp get_all_descendants(parent_span_id, table_name) do |
71 |
| - direct_children = |
72 |
| - :ets.match_object(table_name, {{:child_span, parent_span_id, :_}, :_, :_}) |
73 |
| - |> Enum.map(fn {_key, span_data, _stored_at} -> span_data end) |
| 71 | + defp get_all_descendants(parent_span_id, table_name) do |
| 72 | + direct_children = |
| 73 | + :ets.match_object(table_name, {{:child_span, parent_span_id, :_}, :_, :_}) |
| 74 | + |> Enum.map(fn {_key, span_data, _stored_at} -> span_data end) |
74 | 75 |
|
75 |
| - nested_descendants = |
76 |
| - Enum.flat_map(direct_children, fn child -> |
77 |
| - get_all_descendants(child.span_id, table_name) |
78 |
| - end) |
| 76 | + nested_descendants = |
| 77 | + Enum.flat_map(direct_children, fn child -> |
| 78 | + get_all_descendants(child.span_id, table_name) |
| 79 | + end) |
79 | 80 |
|
80 |
| - (direct_children ++ nested_descendants) |
81 |
| - |> Enum.sort_by(& &1.start_time) |
82 |
| - end |
| 81 | + (direct_children ++ nested_descendants) |
| 82 | + |> Enum.sort_by(& &1.start_time) |
| 83 | + end |
83 | 84 |
|
84 |
| - @spec update_span(SpanRecord.t(), keyword()) :: :ok |
85 |
| - def update_span(%{parent_span_id: parent_span_id} = span_data, opts \\ []) do |
86 |
| - table_name = Keyword.get(opts, :table_name, default_table_name()) |
87 |
| - stored_at = System.system_time(:second) |
| 85 | + @spec update_span(SpanRecord.t(), keyword()) :: :ok |
| 86 | + def update_span(%{parent_span_id: parent_span_id} = span_data, opts \\ []) do |
| 87 | + table_name = Keyword.get(opts, :table_name, default_table_name()) |
| 88 | + stored_at = System.system_time(:second) |
88 | 89 |
|
89 |
| - key = |
90 |
| - if parent_span_id == nil do |
91 |
| - {:root_span, span_data.span_id} |
92 |
| - else |
93 |
| - {:child_span, parent_span_id, span_data.span_id} |
94 |
| - end |
| 90 | + key = |
| 91 | + if parent_span_id == nil do |
| 92 | + {:root_span, span_data.span_id} |
| 93 | + else |
| 94 | + {:child_span, parent_span_id, span_data.span_id} |
| 95 | + end |
95 | 96 |
|
96 |
| - :ets.update_element(table_name, key, [{2, span_data}, {3, stored_at}]) |
| 97 | + :ets.update_element(table_name, key, [{2, span_data}, {3, stored_at}]) |
97 | 98 |
|
98 |
| - :ok |
99 |
| - end |
| 99 | + :ok |
| 100 | + end |
100 | 101 |
|
101 |
| - @spec remove_root_span(String.t(), keyword()) :: :ok |
102 |
| - def remove_root_span(span_id, opts \\ []) do |
103 |
| - table_name = Keyword.get(opts, :table_name, default_table_name()) |
104 |
| - key = {:root_span, span_id} |
| 102 | + @spec remove_root_span(String.t(), keyword()) :: :ok |
| 103 | + def remove_root_span(span_id, opts \\ []) do |
| 104 | + table_name = Keyword.get(opts, :table_name, default_table_name()) |
| 105 | + key = {:root_span, span_id} |
105 | 106 |
|
106 |
| - :ets.select_delete(table_name, [{{key, :_, :_}, [], [true]}]) |
107 |
| - remove_child_spans(span_id, table_name: table_name) |
| 107 | + :ets.select_delete(table_name, [{{key, :_, :_}, [], [true]}]) |
| 108 | + remove_child_spans(span_id, table_name: table_name) |
108 | 109 |
|
109 |
| - :ok |
110 |
| - end |
| 110 | + :ok |
| 111 | + end |
111 | 112 |
|
112 |
| - @spec remove_child_spans(String.t(), keyword()) :: :ok |
113 |
| - def remove_child_spans(parent_span_id, opts) do |
114 |
| - table_name = Keyword.get(opts, :table_name, default_table_name()) |
| 113 | + @spec remove_child_spans(String.t(), keyword()) :: :ok |
| 114 | + def remove_child_spans(parent_span_id, opts) do |
| 115 | + table_name = Keyword.get(opts, :table_name, default_table_name()) |
115 | 116 |
|
116 |
| - :ets.select_delete(table_name, [ |
117 |
| - {{{:child_span, parent_span_id, :_}, :_, :_}, [], [true]} |
118 |
| - ]) |
| 117 | + :ets.select_delete(table_name, [ |
| 118 | + {{{:child_span, parent_span_id, :_}, :_, :_}, [], [true]} |
| 119 | + ]) |
119 | 120 |
|
120 |
| - :ok |
121 |
| - end |
| 121 | + :ok |
| 122 | + end |
122 | 123 |
|
123 |
| - defp schedule_cleanup(interval) do |
124 |
| - Process.send_after(self(), :cleanup_stale_spans, interval) |
125 |
| - end |
| 124 | + defp schedule_cleanup(interval) do |
| 125 | + Process.send_after(self(), :cleanup_stale_spans, interval) |
| 126 | + end |
126 | 127 |
|
127 |
| - defp cleanup_stale_spans(table_name) do |
128 |
| - now = System.system_time(:second) |
129 |
| - cutoff_time = now - @span_ttl |
| 128 | + defp cleanup_stale_spans(table_name) do |
| 129 | + now = System.system_time(:second) |
| 130 | + cutoff_time = now - @span_ttl |
130 | 131 |
|
131 |
| - root_match_spec = [ |
132 |
| - {{{:root_span, :"$1"}, :_, :"$2"}, [{:<, :"$2", cutoff_time}], [:"$1"]} |
133 |
| - ] |
| 132 | + root_match_spec = [ |
| 133 | + {{{:root_span, :"$1"}, :_, :"$2"}, [{:<, :"$2", cutoff_time}], [:"$1"]} |
| 134 | + ] |
134 | 135 |
|
135 |
| - expired_root_spans = :ets.select(table_name, root_match_spec) |
| 136 | + expired_root_spans = :ets.select(table_name, root_match_spec) |
136 | 137 |
|
137 |
| - Enum.each(expired_root_spans, fn span_id -> |
138 |
| - remove_root_span(span_id, table_name: table_name) |
139 |
| - end) |
| 138 | + Enum.each(expired_root_spans, fn span_id -> |
| 139 | + remove_root_span(span_id, table_name: table_name) |
| 140 | + end) |
140 | 141 |
|
141 |
| - child_match_spec = [ |
142 |
| - {{{:child_span, :_, :_}, :_, :"$1"}, [{:<, :"$1", cutoff_time}], [true]} |
143 |
| - ] |
| 142 | + child_match_spec = [ |
| 143 | + {{{:child_span, :_, :_}, :_, :"$1"}, [{:<, :"$1", cutoff_time}], [true]} |
| 144 | + ] |
144 | 145 |
|
145 |
| - :ets.select_delete(table_name, child_match_spec) |
146 |
| - end |
| 146 | + :ets.select_delete(table_name, child_match_spec) |
| 147 | + end |
147 | 148 |
|
148 |
| - defp default_table_name do |
149 |
| - Module.concat(__MODULE__, ETSTable) |
| 149 | + defp default_table_name do |
| 150 | + Module.concat(__MODULE__, ETSTable) |
| 151 | + end |
150 | 152 | end
|
151 | 153 | end
|
0 commit comments