Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Garbage collector for active calls #912

Merged
merged 12 commits into from
May 30, 2022
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion app/controllers/api/calls_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,6 @@ def cancel

queued_calls = QueuedCall.where(call_log_id: call_log.id).all
queued_calls.each do |qc|
qc.cancel_call!
qc.destroy
end

Expand Down
2 changes: 2 additions & 0 deletions broker/include/config_methods.hrl
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
?METHOD_TPL(record_dir).
?METHOD_TPL(seconds_between_calls).
?METHOD_TPL(seconds_for_call_back).
?METHOD_TPL(minutes_between_active_calls_gc_runs).
?METHOD_TPL(minutes_for_cancelling_active_calls).
?METHOD_TPL(db_name).
?METHOD_TPL(db_user).
?METHOD_TPL(db_pass).
Expand Down
6 changes: 5 additions & 1 deletion broker/src/models/call_log.erl
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
-module(call_log).
-export([error/3, info/3, trace/3]).
-export([error/3, info/3, trace/3, cancel_active_calls_started_before/1]).
-define(TABLE_NAME, "call_logs").
-include_lib("erl_dbmodel/include/model.hrl").

Expand All @@ -12,3 +12,7 @@ info(Message, Details, #call_log{id = CallId}) ->
trace(Message, Details, #call_log{id = CallId}) ->
call_log_entry:create("trace", CallId, Message, Details).

cancel_active_calls_started_before(StartedAt) ->
Count = call_log:update_all([{state, "failed"}, {fail_reason, "active-for-too-long"}], [{state, "active"}, {started_at, '<', StartedAt}]),
true = is_number(Count) and (Count > -1),
Count.
45 changes: 45 additions & 0 deletions broker/src/scheduler/active_calls_gc.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
% Active calls GC: Garbage collector for active calls.
% When a call remains active for too long Verboice considers there was an error and cancels it.
% This GC runs every X minutes and cancels every active call for more than N minutes.
% The value X defaults to 10 minutes and is configurable via the ENV variable `minutes_between_active_calls_gc_runs`.
% The value N defaults to 120 minutes and is configurable via the ENV variable `minutes_for_cancelling_active_calls`.
-module(active_calls_gc).
-export([code_change/3, handle_call/3, handle_cast/2, handle_info/2, init/1, start_link/0, terminate/2]).
-compile([{parse_transform, lager_transform}]).
-behaviour(gen_server).

-define(SERVER, ?MODULE).

-define(MILLISECS_IN_ONE_MINUTE, 60000).
-define(INIT_DELAY, ?MILLISECS_IN_ONE_MINUTE).

start_link() ->
gen_server:start_link({local, ?SERVER}, ?MODULE, {}, []).

init({}) ->
erlang:send_after(?INIT_DELAY,self(),cancel_active_calls),
{ok, undefined}.

handle_call(_Request, _From, State) ->
{reply, {error, unknown_call}, State}.

handle_cast(_Msg, State) ->
{noreply, State}.

handle_info(cancel_active_calls, State) ->
N = verboice_config:minutes_for_cancelling_active_calls(),
N_Minutes_Ago = util:seconds_ago(N * 60),
Count = call_log:cancel_active_calls_started_before(N_Minutes_Ago),
lager:info("GC cancelled ~p calls that stayed active for more than ~p minutes", [Count, N]),
Interval = ?MILLISECS_IN_ONE_MINUTE * verboice_config:minutes_between_active_calls_gc_runs(),
erlang:send_after(Interval, self(), cancel_active_calls),
{noreply, State};

handle_info(_Info, State) ->
{noreply, State}.

terminate(_Reason, _State) ->
ok.

code_change(_OldVsn, State, _Extra) ->
{ok, State}.
3 changes: 2 additions & 1 deletion broker/src/scheduler/scheduler_sup.erl
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ init({}) ->
Children = [
?CHILD(channel_mgr, worker),
?CHILD(scheduler, worker),
?CHILD(channel_sup, supervisor)
?CHILD(channel_sup, supervisor),
?CHILD(active_calls_gc, worker)
],
RestartStrategy = {one_for_all, 5, 10},
{ok, {RestartStrategy, Children}}.
5 changes: 4 additions & 1 deletion broker/src/util.erl
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
-module(util).
-export([md5hex/1, to_string/1, binary_to_lower_atom/1, strip_nl/1, binary_to_integer/1, parse_qs/1, normalize_phone_number/1, interpolate/2, interpolate_js/2, to_poirot/1, parse_short_time/1, time_from_now/1, deflate/1, as_binary/1]).
-export([md5hex/1, to_string/1, binary_to_lower_atom/1, strip_nl/1, binary_to_integer/1, parse_qs/1, normalize_phone_number/1, interpolate/2, interpolate_js/2, to_poirot/1, parse_short_time/1, time_from_now/1, seconds_ago/1, deflate/1, as_binary/1]).

md5hex(Data) ->
Hash = crypto:hash(md5, Data),
Expand Down Expand Up @@ -102,6 +102,9 @@ parse_short_time(String) ->
time_from_now(Seconds) ->
calendar:gregorian_seconds_to_datetime(calendar:datetime_to_gregorian_seconds(calendar:universal_time()) + Seconds).

seconds_ago(N) ->
calendar:gregorian_seconds_to_datetime(calendar:datetime_to_gregorian_seconds(calendar:universal_time()) - N).

deflate(Binary) ->
Z = zlib:open(),
zlib:deflateInit(Z),
Expand Down
2 changes: 2 additions & 0 deletions broker/src/verboice_config.erl
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ load_config() ->
load(record_dir, string),
load(seconds_between_calls, int),
load(seconds_for_call_back, int),
load(minutes_between_active_calls_gc_runs, int),
load(minutes_for_cancelling_active_calls, int),
load(db_name, string),
load(db_user, string),
load(db_pass, string),
Expand Down
2 changes: 2 additions & 0 deletions broker/verboice.config
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

{seconds_between_calls, 2},
{seconds_for_call_back, 15},
{minutes_between_active_calls_gc_runs, 10},
{minutes_for_cancelling_active_calls, 120},

{db_name, "verboice_development"},
{db_user, "root"},
Expand Down
2 changes: 2 additions & 0 deletions broker/verboice.config.no-es
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

{seconds_between_calls, 8},
{seconds_for_call_back, 30},
{minutes_between_active_calls_gc_runs, 10},
{minutes_for_cancelling_active_calls, 120},

{db_name, "verboice_development"},
{db_user, "root"},
Expand Down
5 changes: 5 additions & 0 deletions db/migrate/20220526143259_add_index_to_call_logs_on_state.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddIndexToCallLogsOnState < ActiveRecord::Migration
def change
add_index :call_logs, [:state]
end
end
3 changes: 2 additions & 1 deletion db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
#
# It's strongly recommended to check this file into your version control system.

ActiveRecord::Schema.define(:version => 20200420225523) do
ActiveRecord::Schema.define(:version => 20220526143259) do

create_table "accounts", :force => true do |t|
t.string "email", :default => "", :null => false
Expand Down Expand Up @@ -115,6 +115,7 @@
add_index "call_logs", ["call_flow_id"], :name => "index_call_logs_on_call_flow_id"
add_index "call_logs", ["contact_id"], :name => "index_call_logs_on_contact_id"
add_index "call_logs", ["project_id"], :name => "index_call_logs_on_project_id"
add_index "call_logs", ["state"], :name => "index_call_logs_on_state"

create_table "channels", :force => true do |t|
t.integer "account_id"
Expand Down