Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
tree: 17a8178734
Fetching contributors…

Cannot retrieve contributors at this time

241 lines (209 sloc) 10.089 kb
%%% This program is free software; you can redistribute it and/or modify
%%% it under the terms of the GNU General Public License as published by
%%% the Free Software Foundation; either version 2 of the License, or
%%% (at your option) any later version.
%%%
%%% This program is distributed in the hope that it will be useful,
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
%%% GNU General Public License for more details.
%%%
%%% You should have received a copy of the GNU General Public License
%%% along with this program; if not, write to the Free Software
%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
%%%
%%% In addition, as a special exception, you have the permission to
%%% link the code of this program with any library released under
%%% the EPL license and distribute linked combinations including
%%% the two.
%%%-------------------------------------------------------------------
%%% File : ts_session_cache.erl
%%% Author : Nicolas Niclausse <nniclausse@schultze.ird.idealx.com>
%%% Description : cache sessions request from ts_config_server
%%%
%%% Created : 2 Dec 2003 by Nicolas Niclausse <nicolas@niclux.org>
%%%-------------------------------------------------------------------
-module(ts_session_cache).
-behaviour(gen_server).
%%--------------------------------------------------------------------
%% Include files
%%--------------------------------------------------------------------
%%--------------------------------------------------------------------
%% External exports
-export([start/0, get_req/2, get_user_agent/0, add/1, add_match/2]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2,
code_change/3]).
-record(state, {
table, % ets table
hit =0.0, % number of hits
total=0.0, % total number of requests
stats=[], % cache stats msgs
match=[] % cache match logs
}).
-define(DUMP_STATS_INTERVAL, 500). % in milliseconds
-include("ts_profile.hrl").
%%====================================================================
%% External functions
%%====================================================================
%%--------------------------------------------------------------------
%% Function: start_link/0
%% Description: Starts the server
%%--------------------------------------------------------------------
start() ->
?LOG("Starting~n",?INFO),
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
%%--------------------------------------------------------------------
%% Function: get_req/2
%% Description: get next request from session 'Id'
%%--------------------------------------------------------------------
get_req(Id, Count)->
gen_server:call(?MODULE,{get_req, Id, Count}).
%%--------------------------------------------------------------------
%% Function: get_user_agent/0
%%--------------------------------------------------------------------
get_user_agent()->
gen_server:call(?MODULE,{get_user_agent}).
%%--------------------------------------------------------------------
%% Function: add/1
%% Description: Add stats data. Will be accumulated sent periodically
%% to ts_mon
%%--------------------------------------------------------------------
add(Data) ->
gen_server:cast(?MODULE, {add, Data}).
%% @spec add_match(Data::list(),{UserId::integer(),SessionId::integer(),RequestId::integer(),
%% TimeStamp::tuple()}) -> ok
add_match(Data,{UserId,SessionId,RequestId,TimeStamp}) ->
gen_server:cast(?MODULE, {add_match, Data, {UserId,SessionId,RequestId,TimeStamp}}).
%%====================================================================
%% Server functions
%%====================================================================
%%--------------------------------------------------------------------
%% Function: init/1
%% Description: Initiates the server
%% Returns: {ok, State} |
%% {ok, State, Timeout} |
%% ignore |
%% {stop, Reason}
%%--------------------------------------------------------------------
init([]) ->
Table = ets:new(sessiontable, [set, private]),
erlang:start_timer(?DUMP_STATS_INTERVAL, self(), dump_stats ),
{ok, #state{table=Table}}.
%%--------------------------------------------------------------------
%% Function: handle_call/3
%% Description: Handling call messages
%% Returns: {reply, Reply, State} |
%% {reply, Reply, State, Timeout} |
%% {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, Reply, State} | (terminate/2 is called)
%% {stop, Reason, State} (terminate/2 is called)
%%--------------------------------------------------------------------
%% get Nth request from given session Id
handle_call({get_req, Id, N}, _From, State) ->
Tab = State#state.table,
Total = State#state.total+1,
?DebugF("look for ~p th request in session ~p for ~p~n",[N,Id,_From]),
case ets:lookup(Tab, {Id, N}) of
[{_Key, Session}] ->
Hit = State#state.hit+1,
?DebugF("ok, found in cache for ~p~n",[_From]),
?DebugF("hitrate is ~.3f~n",[100.0*Hit/Total]),
{reply, Session, State#state{hit= Hit, total = Total}};
[] -> %% no match, ask the config_server
?DebugF("not found in cache (~p th request in session ~p for ~p)~n",[N,Id,_From]),
case catch ts_config_server:get_req(Id, N) of
{'EXIT',Reason} ->
{reply, {error, Reason}, State};
Reply ->
%% cache the response FIXME: handle bad response ?
ets:insert(Tab, {{Id, N}, Reply}),
{reply, Reply, State#state{total = Total}}
end;
Other -> %%
?LOGF("error ! (~p)~n",[Other],?WARN),
{reply, {error, Other}, State}
end;
handle_call({get_user_agent}, _From, State) ->
Tab = State#state.table,
case ets:lookup(Tab, {http_user_agent, value}) of
[] -> %% no match, ask the config_server
?Debug("user agents not found in cache~n"),
UserAgents = ts_config_server:get_user_agents(),
%% cache the response FIXME: handle bad response ?
?DebugF("Useragents: got from config_server~p~n",[UserAgents]),
ets:insert(Tab, {{http_user_agent, value}, UserAgents}),
{ok, Reply} = choose_user_agent(UserAgents),
{reply, Reply, State};
[{_, [{_Freq, Value}]}] -> %single user agent defined
{reply, Value, State};
[{_, empty }] ->
{reply, "tsung", State};
[{_, UserAgents }] when is_list(UserAgents)->
{ok, Reply} = choose_user_agent(UserAgents),
{reply, Reply, State}
end;
handle_call(_Request, _From, State) ->
Reply = ok,
{reply, Reply, State}.
%%--------------------------------------------------------------------
%% Function: handle_cast/2
%% Description: Handling cast messages
%% Returns: {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State} (terminate/2 is called)
%%--------------------------------------------------------------------
handle_cast({add, Data}, State=#state{stats=List}) when is_list(Data) ->
{noreply, State#state{stats = lists:append(Data, List)} };
handle_cast({add, Data}, State=#state{stats=List}) when is_tuple(Data) ->
{noreply, State#state{stats = lists:append([Data], List)} };
handle_cast({add_match, Data=[First|_Tail],{UserId,SessionId,RequestId,TimeStamp}},
State=#state{stats=List, match=MatchList})->
NewMatchList=lists:append([{UserId,SessionId,RequestId,TimeStamp,First}], MatchList),
{noreply, State#state{stats = lists:append(Data, List), match = NewMatchList}};
handle_cast(_Msg, State) ->
{noreply, State}.
%%--------------------------------------------------------------------
%% Function: handle_info/2
%% Description: Handling all non call/cast messages
%% Returns: {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State} (terminate/2 is called)
%%--------------------------------------------------------------------
handle_info({timeout, _Ref, dump_stats}, State = #state{stats =[]}) ->
erlang:start_timer(?DUMP_STATS_INTERVAL, self(), dump_stats ),
{noreply, State};
handle_info({timeout, _Ref, dump_stats}, State =#state{stats= Stats, match=MatchList}) ->
ts_stats_mon:add(Stats),
ts_match_logger:add(MatchList),
erlang:start_timer(?DUMP_STATS_INTERVAL, self(), dump_stats ),
{noreply, State#state{stats=[],match=[]}};
handle_info(_Info, State) ->
{noreply, State}.
%%--------------------------------------------------------------------
%% Function: terminate/2
%% Description: Shutdown the server
%% Returns: any (ignored by gen_server)
%%--------------------------------------------------------------------
terminate(Reason, _State) ->
?LOGF("Die ! (~p)~n",[Reason],?ERR),
ok.
%%--------------------------------------------------------------------
%% Func: code_change/3
%% Purpose: Convert process state when code is changed
%% Returns: {ok, NewState}
%%--------------------------------------------------------------------
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
choose_user_agent(empty) -> {ok, "tsung"};
choose_user_agent([{_P, Val}]) -> {ok, Val};
choose_user_agent(UserAgents) ->
choose_user_agent(UserAgents, random:uniform(100),0).
choose_user_agent([{P, Val} | _],Rand, Cur) when Rand =< P+Cur->
{ok, Val};
choose_user_agent([{P, _Val} | SList], Rand, Cur) ->
choose_user_agent(SList, Rand, Cur+P).
Jump to Line
Something went wrong with that request. Please try again.