From cd492255bcc11cf7af04dd5dbc0d55dd0e44a601 Mon Sep 17 00:00:00 2001 From: Daniel Finke Date: Wed, 29 Sep 2021 08:32:22 -0700 Subject: [PATCH] PISTON-221: only record in ACDc once per call (#6736) - prevent multiple overlapping recordings if caller re-enters a queue --- applications/acdc/src/acdc_agent_listener.erl | 8 +- .../acdc/src/acdc_recordings_map_srv.erl | 122 ++++++++++++++++++ applications/acdc/src/acdc_recordings_sup.erl | 2 +- applications/acdc/src/acdc_sup.erl | 1 + 4 files changed, 128 insertions(+), 5 deletions(-) create mode 100644 applications/acdc/src/acdc_recordings_map_srv.erl diff --git a/applications/acdc/src/acdc_agent_listener.erl b/applications/acdc/src/acdc_agent_listener.erl index 6d0c1edbd22..f0dcd7a34cc 100644 --- a/applications/acdc/src/acdc_agent_listener.erl +++ b/applications/acdc/src/acdc_agent_listener.erl @@ -1188,10 +1188,10 @@ maybe_start_recording(Call, 'true', Url) -> ,{<<"url">>, Url} ]), lager:debug("starting recording listener for ~s", [Url]), - case acdc_recordings_sup:new(Call, RecordingJObj) of - {'ok', _P} -> - lager:debug("recording tracked in ~p", [_P]); - _E -> lager:debug("failed to start recording: ~p", [_E]) + try acdc_recordings_map_srv:register(Call, RecordingJObj) of + _P -> lager:debug("recording tracked in ~p", [_P]) + catch + 'exit':_E -> lager:debug("failed to start recording: ~p", [_E]) end. recording_format() -> diff --git a/applications/acdc/src/acdc_recordings_map_srv.erl b/applications/acdc/src/acdc_recordings_map_srv.erl new file mode 100644 index 00000000000..cb62f6bf361 --- /dev/null +++ b/applications/acdc/src/acdc_recordings_map_srv.erl @@ -0,0 +1,122 @@ +%%%----------------------------------------------------------------------------- +%%% @copyright (C) 2016-2021, 2600Hz +%%% @doc +%%% @author Daniel Finke +%%% @end +%%%----------------------------------------------------------------------------- +-module(acdc_recordings_map_srv). + +-behaviour(gen_server). + +%% API +-export([start_link/0]). +-export([register/2]). + +%% gen_server callbacks +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, + terminate/2, code_change/3]). + +-include("acdc.hrl"). + +-define(SERVER, ?MODULE). + +-record(state, {table_id :: ets:tid()}). +-type state() :: #state{}. + +%%%============================================================================= +%%% API +%%%============================================================================= + +%%------------------------------------------------------------------------------ +%% @doc Starts the server +%% +%% @end +%%------------------------------------------------------------------------------ +-spec start_link() -> kz_types:startlink_ret(). +start_link() -> + gen_server:start_link({'local', ?SERVER}, ?MODULE, [], []). + +-spec register(kapps_call:call(), kz_json:object()) -> pid(). +register(Call, RecordingJObj) -> + gen_server:call(?SERVER, {'register', Call, RecordingJObj}). + +%%%============================================================================= +%%% gen_server callbacks +%%%============================================================================= + +%%------------------------------------------------------------------------------ +%% @doc Initializes the server +%% +%% @end +%%------------------------------------------------------------------------------ +-spec init([]) -> {'ok', state()}. +init([]) -> + process_flag('trap_exit', 'true'), + TabId = ets:new('map', []), + {'ok', #state{table_id=TabId}}. + +%%------------------------------------------------------------------------------ +%% @doc Handling call messages +%% +%% @end +%%------------------------------------------------------------------------------ +-spec handle_call(any(), kz_term:pid_ref(), state()) -> kz_types:handle_call_ret_state(state()). +handle_call({'register', Call, RecordingJObj}, _From, #state{table_id=TabId}=State) -> + case ets:lookup(TabId, kapps_call:call_id(Call)) of + [] -> + {'ok', Pid} = acdc_recordings_sup:new(Call, RecordingJObj), + link(Pid), + ets:insert(TabId, {kapps_call:call_id(Call), Pid}), + Pid; + [{_, Pid}] -> Pid + end, + {'reply', Pid, State}; +handle_call(_Request, _From, State) -> + Reply = 'ok', + {'reply', Reply, State}. + +%%------------------------------------------------------------------------------ +%% @doc Handling cast messages +%% +%% @end +%%------------------------------------------------------------------------------ +-spec handle_cast(any(), state()) -> kz_types:handle_cast_ret_state(state()). +handle_cast(_Msg, State) -> + {'noreply', State}. + +%%------------------------------------------------------------------------------ +%% @doc Handling all non call/cast messages +%% +%% @end +%%------------------------------------------------------------------------------ +-spec handle_info(any(), state()) -> kz_types:handle_info_ret_state(state()). +handle_info({'EXIT', Pid, _Reason}, #state{table_id=TabId}=State) -> + ets:match_delete(TabId, {'$1', Pid}), + {'noreply', State}; +handle_info(_Info, State) -> + {'noreply', State}. + +%%------------------------------------------------------------------------------ +%% @doc This function is called by a gen_server when it is about to +%% terminate. It should be the opposite of Module:init/1 and do any +%% necessary cleaning up. When it returns, the gen_server terminates +%% with Reason. The return value is ignored. +%% +%% @end +%%------------------------------------------------------------------------------ +-spec terminate(any(), state()) -> 'ok'. +terminate(_Reason, _State) -> + 'ok'. + +%%------------------------------------------------------------------------------ +%% @doc Convert process state when code is changed +%% +%% @end +%%------------------------------------------------------------------------------ +-spec code_change(any(), state(), any()) -> {'ok', state()}. +code_change(_OldVsn, State, _Extra) -> + {'ok', State}. + +%%%============================================================================= +%%% Internal functions +%%%============================================================================= diff --git a/applications/acdc/src/acdc_recordings_sup.erl b/applications/acdc/src/acdc_recordings_sup.erl index 37a2f659320..85e8ee5c71a 100644 --- a/applications/acdc/src/acdc_recordings_sup.erl +++ b/applications/acdc/src/acdc_recordings_sup.erl @@ -20,7 +20,7 @@ -define(SERVER, ?MODULE). --define(CHILDREN, [?WORKER_TYPE('kz_media_recording', 'transient')]). +-define(CHILDREN, [?WORKER_TYPE('kzc_recording', 'transient')]). %%%============================================================================= %%% API functions diff --git a/applications/acdc/src/acdc_sup.erl b/applications/acdc/src/acdc_sup.erl index c92a538240c..3ab6b3a6f11 100644 --- a/applications/acdc/src/acdc_sup.erl +++ b/applications/acdc/src/acdc_sup.erl @@ -28,6 +28,7 @@ ,?WORKER('acdc_agent_manager') ,?WORKER('acdc_init') ,?WORKER('acdc_listener') + ,?WORKER('acdc_recordings_map_srv') ]). %%==============================================================================