@@ -397,7 +397,8 @@ defmodule GenStage do
397
397
we will serve the existing demand, otherwise the event will be queued in
398
398
`GenStage`'s internal buffer. In case events are being queued and not being
399
399
consumed, a log message will be emitted when we exceed the `:buffer_size`
400
- configuration.
400
+ configuration. This behavior can be customized by implementing the optional
401
+ `c:format_discarded/2` callback.
401
402
402
403
While the implementation above is enough to solve the constraints above,
403
404
a more robust implementation would have tighter control over the events
@@ -974,6 +975,15 @@ defmodule GenStage do
974
975
| { :stop , reason , new_state }
975
976
when new_state: term , reason: term
976
977
978
+ @ doc """
979
+ Invoked when items are discarded from the buffer.
980
+
981
+ It receives the number of excess (discarded) items from this invocation.
982
+ This callback returns a boolean that controls whether the default error log for discarded items is printed or not.
983
+ Return true to print the log, return false to skip the log.
984
+ """
985
+ @ callback format_discarded ( discarded :: non_neg_integer , state :: term ) :: boolean
986
+
977
987
@ doc """
978
988
Invoked when a consumer is no longer subscribed to a producer.
979
989
@@ -1122,6 +1132,7 @@ defmodule GenStage do
1122
1132
handle_cancel: 3 ,
1123
1133
handle_demand: 2 ,
1124
1134
handle_events: 3 ,
1135
+ format_discarded: 2 ,
1125
1136
1126
1137
# GenServer
1127
1138
code_change: 3 ,
@@ -1693,6 +1704,14 @@ defmodule GenStage do
1693
1704
"GenStage.stream/1 expects a list of subscriptions, got: #{ inspect ( subscriptions ) } "
1694
1705
end
1695
1706
1707
+ @ doc """
1708
+ Returns the estimated number of buffered items for a producer.
1709
+ """
1710
+ @ spec estimate_buffered_count ( stage , timeout ) :: non_neg_integer
1711
+ def estimate_buffered_count ( stage , timeout \\ 5000 ) do
1712
+ call ( stage , :"$estimate_buffered_count" , timeout )
1713
+ end
1714
+
1696
1715
## Callbacks
1697
1716
1698
1717
@ compile :inline_list_funcs
@@ -1822,15 +1841,19 @@ defmodule GenStage do
1822
1841
consumer_subscribe ( current , to , opts , stage )
1823
1842
end
1824
1843
1844
+ def handle_call ( :"$estimate_buffered_count" , _from , stage ) do
1845
+ producer_estimate_buffered_count ( stage )
1846
+ end
1847
+
1825
1848
def handle_call ( msg , from , % { mod: mod , state: state } = stage ) do
1826
1849
case mod . handle_call ( msg , from , state ) do
1827
1850
{ :reply , reply , events , state } when is_list ( events ) ->
1828
- stage = dispatch_events ( events , length ( events ) , stage )
1829
- { :reply , reply , % { stage | state: state } }
1851
+ stage = dispatch_events ( events , length ( events ) , % { stage | state: state } )
1852
+ { :reply , reply , stage }
1830
1853
1831
1854
{ :reply , reply , events , state , :hibernate } when is_list ( events ) ->
1832
- stage = dispatch_events ( events , length ( events ) , stage )
1833
- { :reply , reply , % { stage | state: state } , :hibernate }
1855
+ stage = dispatch_events ( events , length ( events ) , % { stage | state: state } )
1856
+ { :reply , reply , stage , :hibernate }
1834
1857
1835
1858
{ :stop , reason , reply , state } ->
1836
1859
{ :stop , reason , reply , % { stage | state: state } }
@@ -2106,12 +2129,12 @@ defmodule GenStage do
2106
2129
defp handle_noreply_callback ( return , stage ) do
2107
2130
case return do
2108
2131
{ :noreply , events , state } when is_list ( events ) ->
2109
- stage = dispatch_events ( events , length ( events ) , stage )
2110
- { :noreply , % { stage | state: state } }
2132
+ stage = dispatch_events ( events , length ( events ) , % { stage | state: state } )
2133
+ { :noreply , stage }
2111
2134
2112
2135
{ :noreply , events , state , :hibernate } when is_list ( events ) ->
2113
- stage = dispatch_events ( events , length ( events ) , stage )
2114
- { :noreply , % { stage | state: state } , :hibernate }
2136
+ stage = dispatch_events ( events , length ( events ) , % { stage | state: state } )
2137
+ { :noreply , stage , :hibernate }
2115
2138
2116
2139
{ :stop , reason , state } ->
2117
2140
{ :stop , reason , % { stage | state: state } }
@@ -2210,6 +2233,14 @@ defmodule GenStage do
2210
2233
{ :noreply , stage }
2211
2234
end
2212
2235
2236
+ defp maybe_format_discarded ( mod , excess , state ) do
2237
+ if function_exported? ( mod , :format_discarded , 2 ) do
2238
+ mod . format_discarded ( excess , state )
2239
+ else
2240
+ true
2241
+ end
2242
+ end
2243
+
2213
2244
defp producer_cancel ( ref , kind , reason , stage ) do
2214
2245
% { consumers: consumers , monitors: monitors , state: state } = stage
2215
2246
@@ -2313,21 +2344,41 @@ defmodule GenStage do
2313
2344
stage
2314
2345
end
2315
2346
2316
- defp buffer_events ( events , % { buffer: buffer , buffer_keep: keep } = stage ) do
2347
+ defp buffer_events (
2348
+ events ,
2349
+ % {
2350
+ mod: mod ,
2351
+ buffer: buffer ,
2352
+ buffer_keep: keep ,
2353
+ state: state
2354
+ } = stage
2355
+ ) do
2317
2356
{ buffer , excess , perms } = Buffer . store_temporary ( buffer , events , keep )
2318
2357
2319
2358
case excess do
2320
2359
0 ->
2321
2360
:ok
2322
2361
2323
2362
excess ->
2324
- error_msg = 'GenStage producer ~tp has discarded ~tp events from buffer'
2325
- :error_logger . warning_msg ( error_msg , [ Utils . self_name ( ) , excess ] )
2363
+ if maybe_format_discarded ( mod , excess , state ) do
2364
+ error_msg = 'GenStage producer ~tp has discarded ~tp events from buffer'
2365
+ :error_logger . warning_msg ( error_msg , [ Utils . self_name ( ) , excess ] )
2366
+ end
2326
2367
end
2327
2368
2328
2369
:lists . foldl ( & dispatch_info / 2 , % { stage | buffer: buffer } , perms )
2329
2370
end
2330
2371
2372
+ defp producer_estimate_buffered_count ( % { type: :consumer } = stage ) do
2373
+ error_msg = 'Buffered count can only be requested for producers, GenStage ~tp is a consumer'
2374
+ :error_logger . error_msg ( error_msg , [ Utils . self_name ( ) ] )
2375
+ { :reply , 0 , stage }
2376
+ end
2377
+
2378
+ defp producer_estimate_buffered_count ( % { buffer: buffer } = stage ) do
2379
+ { :reply , Buffer . estimate_size ( buffer ) , stage }
2380
+ end
2381
+
2331
2382
## Info helpers
2332
2383
2333
2384
defp producer_info ( msg , % { type: :consumer } = stage ) do
0 commit comments