Skip to content

Commit 52fb607

Browse files
committed
WIP - rework SpanStorage to use ETS
1 parent e77a601 commit 52fb607

File tree

4 files changed

+78
-64
lines changed

4 files changed

+78
-64
lines changed

lib/sentry/application.ex

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ defmodule Sentry.Application do
1010
config = Config.validate!()
1111
:ok = Config.persist(config)
1212

13+
# Setup ETS tables for span storage
14+
Sentry.Opentelemetry.SpanStorage.setup()
15+
1316
http_client = Keyword.fetch!(config, :client)
1417

1518
maybe_http_client_spec =
@@ -27,7 +30,6 @@ defmodule Sentry.Application do
2730
Sentry.Sources,
2831
Sentry.Dedupe,
2932
Sentry.ClientReport.Sender,
30-
Sentry.Opentelemetry.SpanStorage,
3133
{Sentry.Integrations.CheckInIDMappings,
3234
[
3335
max_expected_check_in_time:

lib/sentry/opentelemetry/span_processor.ex

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ defmodule Sentry.Opentelemetry.SpanProcessor do
6262
contexts: %{
6363
trace: build_trace_context(root_span)
6464
},
65-
spans: Enum.map([root_span | child_spans], &build_span(&1))
65+
spans: [build_span(root_span) | Enum.map(child_spans, &build_span(&1))]
6666
})
6767
end
6868

@@ -304,7 +304,9 @@ defmodule Sentry.Opentelemetry.SpanProcessor do
304304
start_timestamp: span_record.start_time,
305305
timestamp: span_record.end_time,
306306
span_id: span_record.span_id,
307-
parent_span_id: span_record.parent_span_id
307+
parent_span_id: span_record.parent_span_id,
308+
# Add origin to match other span types
309+
origin: span_record.origin
308310
}
309311
end
310312

Lines changed: 51 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,88 +1,78 @@
11
defmodule Sentry.Opentelemetry.SpanStorage do
2-
use GenServer
2+
@moduledoc false
33

4-
def start_link(_opts) do
5-
GenServer.start_link(__MODULE__, nil, name: __MODULE__)
6-
end
7-
8-
def init(_) do
9-
{:ok, %{root_spans: %{}, child_spans: %{}}}
10-
end
4+
@root_spans_table :sentry_root_spans
5+
@child_spans_table :sentry_child_spans
116

12-
def store_span(span_data) do
13-
GenServer.call(__MODULE__, {:store_span, span_data})
14-
end
7+
def setup do
8+
case :ets.whereis(@root_spans_table) do
9+
:undefined ->
10+
:ets.new(@root_spans_table, [:set, :public, :named_table])
1511

16-
def get_root_span(span_id) do
17-
GenServer.call(__MODULE__, {:get_root_span, span_id})
18-
end
12+
_ ->
13+
:ok
14+
end
1915

20-
def get_child_spans(parent_span_id) do
21-
GenServer.call(__MODULE__, {:get_child_spans, parent_span_id})
22-
end
16+
case :ets.whereis(@child_spans_table) do
17+
:undefined ->
18+
:ets.new(@child_spans_table, [:bag, :public, :named_table])
2319

24-
def update_span(span_data) do
25-
GenServer.call(__MODULE__, {:update_span, span_data})
26-
end
20+
_ ->
21+
:ok
22+
end
2723

28-
def remove_span(span_id) do
29-
GenServer.call(__MODULE__, {:remove_span, span_id})
24+
:ok
3025
end
3126

32-
def remove_child_spans(parent_span_id) do
33-
GenServer.call(__MODULE__, {:remove_child_spans, parent_span_id})
34-
end
35-
36-
def handle_call({:store_span, span_data}, _from, state) do
27+
def store_span(span_data) do
3728
if span_data.parent_span_id == nil do
38-
new_state = put_in(state, [:root_spans, span_data.span_id], span_data)
39-
{:reply, :ok, new_state}
29+
:ets.insert(@root_spans_table, {span_data.span_id, span_data})
4030
else
41-
new_state =
42-
update_in(state, [:child_spans, span_data.parent_span_id], fn spans ->
43-
(spans || []) ++ [span_data]
44-
end)
45-
46-
{:reply, :ok, new_state}
31+
:ets.insert(@child_spans_table, {span_data.parent_span_id, span_data})
4732
end
33+
34+
:ok
4835
end
4936

50-
def handle_call({:get_root_span, span_id}, _from, state) do
51-
{:reply, state.root_spans[span_id], state}
37+
def get_root_span(span_id) do
38+
case :ets.lookup(@root_spans_table, span_id) do
39+
[{^span_id, span}] -> span
40+
[] -> nil
41+
end
5242
end
5343

54-
def handle_call({:get_child_spans, parent_span_id}, _from, state) do
55-
{:reply, state.child_spans[parent_span_id] || [], state}
44+
def get_child_spans(parent_span_id) do
45+
:ets.lookup(@child_spans_table, parent_span_id)
46+
|> Enum.map(fn {_parent_id, span} -> span end)
5647
end
5748

58-
def handle_call({:update_span, span_data}, _from, state) do
49+
def update_span(span_data) do
5950
if span_data.parent_span_id == nil do
60-
new_state = put_in(state, [:root_spans, span_data.span_id], span_data)
61-
{:reply, :ok, new_state}
51+
:ets.insert(@root_spans_table, {span_data.span_id, span_data})
6252
else
63-
new_state =
64-
update_in(state, [:child_spans, span_data.parent_span_id], fn spans ->
65-
Enum.map(spans || [], fn span ->
66-
if span.span_id == span_data.span_id, do: span_data, else: span
67-
end)
68-
end)
69-
70-
{:reply, :ok, new_state}
53+
existing_spans = :ets.lookup(@child_spans_table, span_data.parent_span_id)
54+
55+
:ets.delete(@child_spans_table, span_data.parent_span_id)
56+
57+
Enum.each(existing_spans, fn {parent_id, span} ->
58+
if span.span_id != span_data.span_id do
59+
:ets.insert(@child_spans_table, {parent_id, span})
60+
end
61+
end)
62+
63+
:ets.insert(@child_spans_table, {span_data.parent_span_id, span_data})
7164
end
72-
end
7365

74-
def handle_call({:remove_span, span_id}, _from, state) do
75-
new_state = %{
76-
state
77-
| root_spans: Map.delete(state.root_spans, span_id),
78-
child_spans: Map.delete(state.child_spans, span_id)
79-
}
66+
:ok
67+
end
8068

81-
{:reply, :ok, new_state}
69+
def remove_span(span_id) do
70+
:ets.delete(@root_spans_table, span_id)
71+
:ok
8272
end
8373

84-
def handle_call({:remove_child_spans, parent_span_id}, _from, state) do
85-
new_state = %{state | child_spans: Map.delete(state.child_spans, parent_span_id)}
86-
{:reply, :ok, new_state}
74+
def remove_child_spans(parent_span_id) do
75+
:ets.delete(@child_spans_table, parent_span_id)
76+
:ok
8777
end
8878
end

test/sentry/opentelemetry/span_processor_test.exs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,26 @@ defmodule Sentry.Opentelemetry.SpanProcessorTest do
33

44
import Sentry.TestHelpers
55

6+
alias Sentry.Opentelemetry.SpanStorage
7+
8+
setup do
9+
# Create tables
10+
SpanStorage.setup()
11+
12+
on_exit(fn ->
13+
# Only try to clean up tables if they exist
14+
if :ets.whereis(:sentry_root_spans) != :undefined do
15+
:ets.delete_all_objects(:sentry_root_spans)
16+
end
17+
18+
if :ets.whereis(:sentry_child_spans) != :undefined do
19+
:ets.delete_all_objects(:sentry_child_spans)
20+
end
21+
end)
22+
23+
:ok
24+
end
25+
626
defmodule TestEndpoint do
727
require OpenTelemetry.Tracer, as: Tracer
828

0 commit comments

Comments
 (0)