Skip to content

Commit b0fdb25

Browse files
authored
feat: add telemetry metric for process memory usage (#43)
1 parent 1409ddc commit b0fdb25

File tree

4 files changed

+97
-2
lines changed

4 files changed

+97
-2
lines changed

lib/buffy/throttle.ex

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,14 @@ defmodule Buffy.Throttle do
9090
9191
- `:result` - The return value of the `handle_throttle/1` function.
9292
93+
### Memory Leaks
94+
95+
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.
96+
97+
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.
98+
99+
summary("buffy.throttle.total_heap_size", tags: [:module])
100+
93101
"""
94102

95103
@typedoc """
@@ -216,7 +224,32 @@ defmodule Buffy.Throttle do
216224
@spec init(Buffy.Throttle.state()) :: {:ok, Buffy.Throttle.state()}
217225
def init({key, args}) do
218226
Process.send_after(self(), :timeout, unquote(throttle))
219-
{:ok, {key, args}}
227+
{:ok, {key, args}, {:continue, :measure_memory}}
228+
end
229+
230+
@doc false
231+
@impl GenServer
232+
@spec handle_continue(:measure_memory, Buffy.Throttle.state()) :: {:noreply, Buffy.Throttle.state()}
233+
def handle_continue(:measure_memory, {key, args} = state) do
234+
case Process.info(self(), [:total_heap_size]) do
235+
[{:total_heap_size, total_heap_size}] ->
236+
:telemetry.execute(
237+
[:buffy, :throttle],
238+
%{
239+
total_heap_size: total_heap_size
240+
},
241+
%{
242+
args: args,
243+
key: key,
244+
module: __MODULE__
245+
}
246+
)
247+
248+
_ ->
249+
nil
250+
end
251+
252+
{:noreply, state}
220253
end
221254

222255
@doc false

lib/buffy/throttle_and_timed.ex

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,14 @@ defmodule Buffy.ThrottleAndTimed do
189189
190190
- `:result` - The return value of the `handle_throttle/1` function.
191191
192+
### Memory Leaks
193+
194+
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.
195+
196+
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.
197+
198+
summary("buffy.throttle.total_heap_size", tags: [:module])
199+
192200
"""
193201
require Logger
194202

@@ -339,7 +347,33 @@ defmodule Buffy.ThrottleAndTimed do
339347
@impl GenServer
340348
@spec init({Buffy.ThrottleAndTimed.key(), Buffy.ThrottleAndTimed.args()}) :: {:ok, Buffy.ThrottleAndTimed.state()}
341349
def init({key, args}) do
342-
{:ok, schedule_throttle_and_update_state(%{key: key, args: args, timer_ref: nil})}
350+
{:ok, schedule_throttle_and_update_state(%{key: key, args: args, timer_ref: nil}), {:continue, :measure_memory}}
351+
end
352+
353+
@doc false
354+
@impl GenServer
355+
@spec handle_continue(:measure_memory, Buffy.ThrottleAndTimed.state()) ::
356+
{:noreply, Buffy.ThrottleAndTimed.state()}
357+
def handle_continue(:measure_memory, state) do
358+
case Process.info(self(), [:total_heap_size]) do
359+
[{:total_heap_size, total_heap_size}] ->
360+
:telemetry.execute(
361+
[:buffy, :throttle],
362+
%{
363+
total_heap_size: total_heap_size
364+
},
365+
%{
366+
args: state.args,
367+
key: state.key,
368+
module: __MODULE__
369+
}
370+
)
371+
372+
_ ->
373+
nil
374+
end
375+
376+
{:noreply, state}
343377
end
344378

345379
@doc """

test/buffy/throttle_and_timed_test.exs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,7 @@ defmodule Buffy.ThrottleAndTimedTest do
248248
describe ":telemetry" do
249249
setup do
250250
:telemetry_test.attach_event_handlers(self(), [
251+
[:buffy, :throttle],
251252
[:buffy, :throttle, :throttle],
252253
[:buffy, :throttle, :timeout],
253254
[:buffy, :throttle, :handle, :start],
@@ -260,6 +261,19 @@ defmodule Buffy.ThrottleAndTimedTest do
260261
:ok
261262
end
262263

264+
test "emits [:buffy, :throttle] total_heap_size" do
265+
MyZeroThrottler.throttle(:foo)
266+
267+
assert_receive {[:buffy, :throttle], _ref, %{total_heap_size: heap},
268+
%{
269+
args: :foo,
270+
key: _,
271+
module: MyZeroThrottler
272+
}}
273+
274+
assert heap > 0
275+
end
276+
263277
test "emits [:buffy, :throttle, :throttle]" do
264278
MyZeroThrottler.throttle(:foo)
265279

test/buffy/throttle_test.exs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ defmodule Buffy.ThrottleTest do
3131
setup do
3232
_ref =
3333
:telemetry_test.attach_event_handlers(self(), [
34+
[:buffy, :throttle],
3435
[:buffy, :throttle, :throttle],
3536
[:buffy, :throttle, :handle, :jitter],
3637
[:buffy, :throttle, :handle, :start],
@@ -41,6 +42,19 @@ defmodule Buffy.ThrottleTest do
4142
:ok
4243
end
4344

45+
test "emits [:buffy, :throttle] total_heap_size" do
46+
MyZeroThrottler.throttle(:foo)
47+
48+
assert_receive {[:buffy, :throttle], _ref, %{total_heap_size: heap},
49+
%{
50+
args: :foo,
51+
key: _,
52+
module: MyZeroThrottler
53+
}}
54+
55+
assert heap > 0
56+
end
57+
4458
test "emits [:buffy, :throttle, :throttle]" do
4559
MyZeroThrottler.throttle(:foo)
4660

0 commit comments

Comments
 (0)