Examples

HTTP

[{"test" : {"id" : "payment-test",
            "phases" : ["payment-phase"],
            "plugins" : ["payment-server"]
           }},

 {"plugin" : {"id" : "payment-server",
              "plugin_info" 
              : {"http-plugin" : {"servers" : [{"host" : "10.100.0.120",
                                                "port" : 5050,
                                                "ssl" : false}
                                              ]
                                 }
                }
             }
 },

 {"phase" : {"id" : "payment-phase",
             "arrival_rate" : 10,
             "duration" : 600000,
             "concurrent_scenarios" : 250,
             "rate" : 500,
             "scenarios" : [{"payment-scenario" : 1}]}},

 {"scenario" :
  {"id" : "payment-scenario",
   "actions" : [ 
       {"data-csv" : {"file" : "users_list.csv",
                      "name" : "all_users"
                     }
       },
       {"data-csv_value" : {"name" : "all_users",
                            "fields" : [1, 2],
                            "vars" : ["username", "password"]
                           }
       },
       {"http-request" : {"plugin_id" : "payment-server",
                          "method" : "POST",
                          "path" : "/shop/session",
                          "body" : {"jsonpath-multiset" :
                                {
                                  "object" :
                                       "{\"username\": \"user\",
                                         \"password\": \"password\"}",
                                  "pairs" : [
                                      {"value" : {"var-get" : {"name" : "username"}},
                                       "path" : "username"},
                                      {"value" : {"var-get" : {"name" : "password"}},
                                       "path" : "password"}
                                  ]
                                }
                                  }
                         }
       },
       {"jsonpath-get" : {"path" : "headers.location",
                          "variable" : "shoppingSession"
                         }
       },
       {"http-request" : {"plugin_id" : "payment-server",
                          "method" : "GET",
                          "path" : {"var-get" : {"name" :
                                                 "shoppingSession"}}
                         }
       },
       {"http-request" : {"plugin_id" : "payment-server",
                          "method" : "GET",
                          "path" : {"url-join" :
                                      { "root" : "/shop/user",
                                        "path" : {"var-get" : {"name" : "username"}
                                                 }
                                      }
                                   }
                         }
       },
       {"http-request" : {"plugin_id" : "payment-server",
                          "method" : "POST",
                          "path" : "/shop/basket/sonyDSCW800compactdigitalcamera",
                          "body" : "{\"price\": {\"amount\" : 59,
                                                 \"currency\": \"GBP\"}}"
                         }
       },
       {"jsonpath-get" : {"path" : "headers.location",
                          "variable" : "productDetails"
                         }
       },
       {"http-request" : {"plugin_id" : "payment-server",
                          "method" : "GET",
                          "path" : {"var-get" : {"name" : "productDetails"}}
                         }
       },    
       {"http-request" : {"plugin_id" : "payment-server",
                          "method" : "GET",
                          "path" : "/shop/payment"
                         }
       },
       {"jsonpath-get" : {"path" : "body.payment_methods[0].uri",
                          "variable" : "paymentMethod"
                         }
       },
       {"http-request" : {"plugin_id" : "payment-server",
                          "method" : "POST",
                          "path" : {"var-get" : {"name" : "paymentMethod"}},
                          "body" : "{\"amount\" : 59, \"currency\" : \"GBP\",
                                     \"security_code\" : \"123\"}"
                         }
       }
   ]}}
]

XMPP

#!/usr/bin/env escript

%% Adapted from https://github.com/esl/amoc

-include_lib("exml/include/exml.hrl").

%% ===================================================================
-define(HOST, <<"localhost">>). %% The virtual host served by the server
-define(SERVER_IPS, {<<"127.0.0.1">>}). %% Tuple of servers, for example {<<"10.100.0.21">>, <<"10.100.0.22">>}
-define(CHECKER_SESSIONS_INDICATOR, 10). %% How often a checker session should be generated
-define(SLEEP_TIME_AFTER_SCENARIO, 10000). %% wait 10s after scenario before disconnecting
-define(NUMBER_OF_PREV_NEIGHBOURS, 4).
-define(NUMBER_OF_NEXT_NEIGHBOURS, 4).
-define(NUMBER_OF_SEND_MESSAGE_REPEATS, 73).
-define(SLEEP_TIME_AFTER_EVERY_MESSAGE, 20000).


%% ===================================================================
%% Counters and histograms
-define(MESSAGES_CT, [<<"xmpp">>, <<"counter">>, <<"messages_sent">>]).
-define(MESSAGE_TTD_H, [<<"xmpp">>, <<"histogram">>, <<"message_ttd">>]).
-define(CONNECTION_CT, [<<"xmpp">>, <<"counter">>, <<"connections">>]).
-define(CONNECTION_H, [<<"xmpp">>, <<"histogram">>, <<"connection">>]).
-define(CONNECTION_FAILURE_CT, [<<"xmpp">>, <<"counter">>, <<"connection_failures">>]).


%% ===================================================================
%% Define initial values.
%% The regulation will use the load_regulation_targets/2 callback afterwards
-rate(150).
-workers(100).
-arrival_rate(20). %% Number of new scenarios started per second
%% Set total of unique ids, will be split among nodes
%% Used to generate the user ids later on. Retrieved from the process
%% dictionary erlang:get(unique_id).
-unique_ids(15000).
%% ===================================================================

main([]) ->
    MyId = erlang:get(unique_id),
    Cfg = make_user(MyId, <<"res1">>),

    IsChecker = MyId rem ?CHECKER_SESSIONS_INDICATOR == 0,

    {ConnectionTime, ConnectionResult} = timer:tc(escalus_connection, start, [Cfg]),
    Client = case ConnectionResult of
                 {ok, ConnectedClient, _, _} ->
                     loader_metrics:update_counter(?CONNECTION_CT, 1),
                     loader_metrics:update_histogram(?CONNECTION_H, ConnectionTime),
                     ConnectedClient;
                 _Error ->
                     loader_metrics:update_counter(?CONNECTION_FAILURE_CT, 1),
                     throw(abort_scenario)
             end,

    do(IsChecker, MyId, Client),

    timer:sleep(?SLEEP_TIME_AFTER_SCENARIO),
    send_presence_unavailable(Client),
    escalus_connection:stop(Client).

%% This callback may return a list of metrics to regulate the load.
%% Otherwise, it's also useful to create all XMPP specific metrics.
init_load_regulation_metrics() ->
    loader_metrics:new_counter(?MESSAGES_CT),
    loader_metrics:new_histogram(?MESSAGE_TTD_H),
    loader_metrics:new_counter(?CONNECTION_CT),
    loader_metrics:new_counter(?CONNECTION_FAILURE_CT),
    loader_metrics:new_histogram(?CONNECTION_H),
    {ok, []}.

%% Regulation callback. Megaload will use the number of workers and rate
%% to set the maximum generated by the system. These could be capped lower
%% if the machine is not able to cope with the amount of load.
%% The gauges 'global_gauge_maxRate' and 'global_gauge_maxWorker' will show
%% this capped limits in the UI.
%% Megaload automatically regulates the number of workers, however the rate
%% will depend on the scenario described in the escript. Regulation will
%% only be triggered by short-lived scenarios.
%% Nonetheless, this kind of example regulates the rate with its
%% own timers, so built-in Megaload regulation would not be needed.
load_regulation_targets(_Metrics,_OldTargets) ->
    NewTargets = {{workers, 1500}, {rate, 3000}},
    {ok, NewTargets}.

user_spec(ProfileId, Password, Res) ->    
    [ {username, ProfileId},
      {server, ?HOST},
      {host, pick_server(?SERVER_IPS)},
      {password, Password},
      {carbons, false},
      {stream_management, false},
      {resource, Res}
    ].

make_user(Id, R) ->
    BinId = integer_to_binary(Id),
    ProfileId = <<"user_", BinId/binary>>,
    Password = <<"password_", BinId/binary>>,
    user_spec(ProfileId, Password, R).

do(false, MyId, Client) ->
    escalus_connection:set_filter_predicate(Client, none),

    send_presence_available(Client),
    timer:sleep(5000),

    NeighbourIds = lists:delete(MyId, lists:seq(max(1,MyId-?NUMBER_OF_PREV_NEIGHBOURS),
                                                MyId+?NUMBER_OF_NEXT_NEIGHBOURS)),
    send_messages_many_times(Client, ?SLEEP_TIME_AFTER_EVERY_MESSAGE, NeighbourIds);
do(_Other, _MyId, Client) ->
    send_presence_available(Client),
    receive_forever(Client).

receive_forever(Client) ->
    Stanza = escalus_connection:get_stanza(Client, message, infinity),
    Now = from_now(os:timestamp()),
    case Stanza of
        #xmlel{name = <<"message">>, attrs=Attrs} ->
            case lists:keyfind(<<"timestamp">>, 1, Attrs) of
                {_, Sent} ->
                    TTD = (Now - binary_to_integer(Sent)),
                    loader_metrics:update_histogram(?MESSAGE_TTD_H, TTD);
                _ ->
                    ok
            end;
        _ ->
            ok
    end,
    receive_forever(Client).


send_presence_available(Client) ->
    Pres = escalus_stanza:presence(<<"available">>),
    escalus_connection:send(Client, Pres).

send_presence_unavailable(Client) ->
    Pres = escalus_stanza:presence(<<"unavailable">>),
    escalus_connection:send(Client, Pres).

send_messages_many_times(Client, MessageInterval, NeighbourIds) ->
    S = fun(_) ->
                send_messages_to_neighbors(Client, NeighbourIds, MessageInterval)
        end,
    lists:foreach(S, lists:seq(1, ?NUMBER_OF_SEND_MESSAGE_REPEATS)).


send_messages_to_neighbors(Client, TargetIds, SleepTime) ->
    [send_message(Client, make_jid(TargetId), SleepTime)
     || TargetId <- TargetIds].

send_message(Client, ToId, SleepTime) ->
    MsgIn = make_message(ToId),
    TimeStamp = integer_to_binary(from_now(os:timestamp())),
    escalus_connection:send(Client, escalus_stanza:setattr(MsgIn, <<"timestamp">>, TimeStamp)),
    loader_metrics:update_counter(?MESSAGES_CT, 1),
    %% Increases counters of total, successful requests and histograms of
    %% requests per second. 
    loader_requests:inc_successful(),
    timer:sleep(SleepTime).

make_message(ToId) ->
    Body = <<"hello sir, you are a gentelman and a scholar.">>,
    Id = escalus_stanza:id(),
    escalus_stanza:set_id(escalus_stanza:chat_to(ToId, Body), Id).

make_jid(Id) ->
    BinInt = integer_to_binary(Id),
    ProfileId = <<"user_", BinInt/binary>>,
    Host = ?HOST,
    << ProfileId/binary, "@", Host/binary >>.

pick_server(Servers) ->
    S = size(Servers),
    N = erlang:phash2(self(), S) + 1,
    element(N, Servers).

from_now({MegaSecs,Secs,Usecs}) ->
    (MegaSecs * 1000000 + Secs) * 1000000 + Usecs.