Skip to content

Commit

Permalink
feat: add telemetry metric for process memory usage (#43)
Browse files Browse the repository at this point in the history
  • Loading branch information
btkostner authored Apr 10, 2024
1 parent 1409ddc commit b0fdb25
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 2 deletions.
35 changes: 34 additions & 1 deletion lib/buffy/throttle.ex
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,14 @@ defmodule Buffy.Throttle do
- `:result` - The return value of the `handle_throttle/1` function.
### Memory Leaks
With any sort of debounce and Elixir processes, you need to be careful about handling too many processes, or having to much state in memory at the same time. If you handle large amounts of data there is a good chance you'll end up with high memory usage and possibly affect other parts of your system.
To help monitor this usage, Buffy has a telemetry metric that measures the Elixir process memory usage. If you summarize this metric you should get a good view into your buffy throttle processes.
summary("buffy.throttle.total_heap_size", tags: [:module])
"""

@typedoc """
Expand Down Expand Up @@ -216,7 +224,32 @@ defmodule Buffy.Throttle do
@spec init(Buffy.Throttle.state()) :: {:ok, Buffy.Throttle.state()}
def init({key, args}) do
Process.send_after(self(), :timeout, unquote(throttle))
{:ok, {key, args}}
{:ok, {key, args}, {:continue, :measure_memory}}
end

@doc false
@impl GenServer
@spec handle_continue(:measure_memory, Buffy.Throttle.state()) :: {:noreply, Buffy.Throttle.state()}
def handle_continue(:measure_memory, {key, args} = state) do
case Process.info(self(), [:total_heap_size]) do
[{:total_heap_size, total_heap_size}] ->
:telemetry.execute(
[:buffy, :throttle],
%{
total_heap_size: total_heap_size
},
%{
args: args,
key: key,
module: __MODULE__
}
)

_ ->
nil
end

{:noreply, state}
end

@doc false
Expand Down
36 changes: 35 additions & 1 deletion lib/buffy/throttle_and_timed.ex
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,14 @@ defmodule Buffy.ThrottleAndTimed do
- `:result` - The return value of the `handle_throttle/1` function.
### Memory Leaks
With any sort of debounce and Elixir processes, you need to be careful about handling too many processes, or having to much state in memory at the same time. If you handle large amounts of data there is a good chance you'll end up with high memory usage and possibly affect other parts of your system.
To help monitor this usage, Buffy has a telemetry metric that measures the Elixir process memory usage. If you summarize this metric you should get a good view into your buffy throttle processes.
summary("buffy.throttle.total_heap_size", tags: [:module])
"""
require Logger

Expand Down Expand Up @@ -339,7 +347,33 @@ defmodule Buffy.ThrottleAndTimed do
@impl GenServer
@spec init({Buffy.ThrottleAndTimed.key(), Buffy.ThrottleAndTimed.args()}) :: {:ok, Buffy.ThrottleAndTimed.state()}
def init({key, args}) do
{:ok, schedule_throttle_and_update_state(%{key: key, args: args, timer_ref: nil})}
{:ok, schedule_throttle_and_update_state(%{key: key, args: args, timer_ref: nil}), {:continue, :measure_memory}}
end

@doc false
@impl GenServer
@spec handle_continue(:measure_memory, Buffy.ThrottleAndTimed.state()) ::
{:noreply, Buffy.ThrottleAndTimed.state()}
def handle_continue(:measure_memory, state) do
case Process.info(self(), [:total_heap_size]) do
[{:total_heap_size, total_heap_size}] ->
:telemetry.execute(
[:buffy, :throttle],
%{
total_heap_size: total_heap_size
},
%{
args: state.args,
key: state.key,
module: __MODULE__
}
)

_ ->
nil
end

{:noreply, state}
end

@doc """
Expand Down
14 changes: 14 additions & 0 deletions test/buffy/throttle_and_timed_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ defmodule Buffy.ThrottleAndTimedTest do
describe ":telemetry" do
setup do
:telemetry_test.attach_event_handlers(self(), [
[:buffy, :throttle],
[:buffy, :throttle, :throttle],
[:buffy, :throttle, :timeout],
[:buffy, :throttle, :handle, :start],
Expand All @@ -260,6 +261,19 @@ defmodule Buffy.ThrottleAndTimedTest do
:ok
end

test "emits [:buffy, :throttle] total_heap_size" do
MyZeroThrottler.throttle(:foo)

assert_receive {[:buffy, :throttle], _ref, %{total_heap_size: heap},
%{
args: :foo,
key: _,
module: MyZeroThrottler
}}

assert heap > 0
end

test "emits [:buffy, :throttle, :throttle]" do
MyZeroThrottler.throttle(:foo)

Expand Down
14 changes: 14 additions & 0 deletions test/buffy/throttle_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ defmodule Buffy.ThrottleTest do
setup do
_ref =
:telemetry_test.attach_event_handlers(self(), [
[:buffy, :throttle],
[:buffy, :throttle, :throttle],
[:buffy, :throttle, :handle, :jitter],
[:buffy, :throttle, :handle, :start],
Expand All @@ -41,6 +42,19 @@ defmodule Buffy.ThrottleTest do
:ok
end

test "emits [:buffy, :throttle] total_heap_size" do
MyZeroThrottler.throttle(:foo)

assert_receive {[:buffy, :throttle], _ref, %{total_heap_size: heap},
%{
args: :foo,
key: _,
module: MyZeroThrottler
}}

assert heap > 0
end

test "emits [:buffy, :throttle, :throttle]" do
MyZeroThrottler.throttle(:foo)

Expand Down

0 comments on commit b0fdb25

Please sign in to comment.