Permalink
Browse files

base

  • Loading branch information...
0 parents commit e4ce25dda74958e963c75be8612845cd2dcfff40 @wagerlabs wagerlabs committed Jul 15, 2009
Showing with 2,197 additions and 0 deletions.
  1. +1 −0 Emakefile
  2. +71 −0 Makefile
  3. +63 −0 README.textile
  4. +38 −0 ebin/janus.app
  5. +9 −0 ebin/janus.rel
  6. +3 −0 priv/public_name
  7. +3 −0 priv/user_data
  8. +146 −0 src/barrier.erl
  9. +35 −0 src/bin.erl
  10. +188 −0 src/bot.erl
  11. +157 −0 src/client_proxy.erl
  12. +31 −0 src/common.erl
  13. +203 −0 src/flashbot.erl
  14. +42 −0 src/histo.erl
  15. +43 −0 src/janus.erl
  16. +143 −0 src/janus_acceptor.erl
  17. +89 −0 src/janus_admin.erl
  18. +105 −0 src/janus_app.erl
  19. +91 −0 src/janus_flash.erl
  20. +105 −0 src/launcher.erl
  21. +127 −0 src/mapper.erl
  22. +113 −0 src/pubsub.erl
  23. +73 −0 src/t.erl
  24. +146 −0 src/topman.erl
  25. +125 −0 src/transport.erl
  26. +47 −0 src/util.erl
@@ -0,0 +1 @@
+{'src/*', [{i, "include"}, {outdir, "ebin"}]}.
@@ -0,0 +1,71 @@
+LOAD_PATH = \
+ ebin \
+ mochiweb/ebin \
+ $(NULL)
+
+MNESIA_DIR = /tmp/janus.db
+
+NAME = janus
+HOST = `hostname`
+NODE = $(NAME)@$(HOST)
+EC2_USER_DATA = `priv/user_data`
+
+BASIC_OPTS = \
+ -pa $(LOAD_PATH) \
+ +A 8 +K true +P 60000 -smp disable \
+ $(NULL)
+
+OPTS = \
+ $(BASIC_OPTS) \
+ -mnesia dir '"$(MNESIA_DIR)"' \
+ -s mnesia start \
+ $(NULL)
+
+LOCAL_OPTS = \
+ $(OPTS) \
+ -janus cluster $(CLUSTER) \
+ -janus listen_port 8081 \
+ $(EXTRA_OPTS) \
+ $(NULL)
+
+EC2_OPTS = \
+ $(OPTS) \
+ -janus cluster "'$(EC2_USER_DATA)'" \
+ $(EXTRA_OPTS) \
+ $(NULL)
+
+all: compile
+
+compile:
+ erl -make
+
+make_boot: compile
+ erl $(BASIC_OPTS) -s janus_admin make_boot -s init stop
+
+run: compile
+ erl $(LOCAL_OPTS) -name $(NODE) -s janus start
+
+run1: compile
+ erl $(LOCAL_OPTS) -name $(NODE) -s janus start
+
+run2: compile
+ erl $(LOCAL_OPTS) -name $(NODE) -boot janus
+
+ec2: compile make_boot
+ erl $(EC2_OPTS) -name $(NODE) -boot janus
+
+remsh:
+ erl $(BASIC_OPTS) -name remote -remsh $(NODE)
+
+sh: compile
+ erl $(LOCAL_OPTS) -name debug
+
+sh1: compile
+ erl $(LOCAL_OPTS) -name debug1 -remsh debug@$(NODE)
+
+clean:
+ rm -rf ebin/*.beam
+ rm -fr $(MNESIA_DIR)
+
+
+
@@ -0,0 +1,63 @@
+h1. Janus
+
+Janus is a messaging server optimized to unicast over TCP to thousands of clients subscribed to topics of interest.
+
+The ultimate goal is to maintain a latency of less than 2 seconds for 20 thousand clients on Amazon EC2 (small instance).
+
+h1. License
+
+Janus is available under the MIT license.
+
+h1. Bounty and rules
+
+1) Unicast to 20K clients in < 1s (max latency) nets you a $1000 bounty if you are the first person to achieve this. Unicast in < 2s (max latency) nets you a $500 bounty.
+
+2) Final proof has to be from EC2, one small instance for the server and one for the bots.
+
+3) {packet, 0}
+
+4) TCP, not UDP.
+
+5) No change of the protocol unless I agree to it.
+
+h1. Installation
+
+Janus requires at least OTP R13B01.
+
+Create a soft link from janus/mochiweb to your Mochiweb installation.
+
+h1. Running
+
+1) 'make run1' to start the server
+2) 'make sh' in a different window to run clients
+3) 'bot:test(flashbot, 20000).' to run 20k bots on the same machine
+4) 'bot:test(flashbot, 20000, 'host', 8081).' if the server is somewhere else
+
+You should see output like this if everything goes well (MacBook Pro Core2Duo, 2.93Ghz, SSD):
+
+<pre><code>
+(debug@biggie.local)5> bot:test(flashbot,10000).
+
+=INFO REPORT==== 15-Jul-2009::19:44:20 ===
+setup: 5055.70ms, n: 10000, run: 7524.29ms
+ 1.8690ms | min
+ 500.0000ms | 2125 - 21.25%
+ 1000.0000ms | 5010 - 50.10%
+ 1500.0000ms | 2865 - 28.65%
+ 1269.7850ms | max
+ok
+(debug@biggie.local)5> bot:test(flashbot,20000).
+
+=INFO REPORT==== 15-Jul-2009::19:44:55 ===
+setup: 14293.01ms, n: 20000, run: 21956.94ms
+ 2.4850ms | min
+ 500.0000ms | 478 - 2.39%
+ 1000.0000ms | 2283 - 11.42%
+ 1500.0000ms | 7154 - 35.77%
+ 2000.0000ms | 1574 - 7.87%
+ 2500.0000ms | 3301 - 16.50%
+ 3000.0000ms | 2779 - 13.89%
+ 3500.0000ms | 2431 - 12.16%
+ 3277.0740ms | max
+ok
+</code></pre>
@@ -0,0 +1,38 @@
+{application, janus,
+ [{description, "Janus"},
+ {vsn, "0.0.1"},
+ {id, "janus"},
+ {modules, [barrier,
+ bin,
+ bot,
+ client_proxy,
+ common,
+ flashbot,
+ histo,
+ janus,
+ janus_acceptor,
+ janus_admin,
+ janus_app,
+ janus_flash,
+ launcher,
+ mapper,
+ pubsub,
+ topman,
+ t,
+ transport,
+ util
+ ]},
+ {registered, [janus_sup,
+ janus_topman_sup,
+ janus_proxy_mapper_sup,
+ janus_transport_sup,
+ janus_listener]},
+ {applications, [kernel,
+ stdlib,
+ mnesia,
+ inets
+ ]},
+ {mod, {janus_app, []}},
+ {env, []}
+ ]
+}.
@@ -0,0 +1,9 @@
+{release, {"Janus","1.0.0"}, {erts, "5.7.2"},
+ [{kernel,"2.13.2"},
+ {stdlib,"1.16.2"},
+ {mnesia, "4.4.10"},
+ {sasl, "2.1.6"},
+ {os_mon, "2.2.2"},
+ {inets, "5.1"},
+ {janus,"1.0.0"}
+ ]}.
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+curl -s http://169.254.169.254/2009-04-04/meta-data/public-hostname
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+curl -fs http://169.254.169.254/latest/user-data
@@ -0,0 +1,146 @@
+%%% Copyright (c) 2009 Oortle, Inc
+
+%%% Permission is hereby granted, free of charge, to any person
+%%% obtaining a copy of this software and associated documentation
+%%% files (the "Software"), to deal in the Software without restriction,
+%%% including without limitation the rights to use, copy, modify, merge,
+%%% publish, distribute, sublicense, and/or sell copies of the Software,
+%%% and to permit persons to whom the Software is furnished to do so,
+%%% subject to the following conditions:
+
+%%% The above copyright notice and this permission notice shall be included
+%%% in all copies or substantial portions of the Software.
+
+%%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+%%% OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+%%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+%%% THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+%%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+%%% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+%%% DEALINGS IN THE SOFTWARE.
+
+-module(barrier).
+-behaviour(gen_server).
+
+-export([start/2, stop/1, bump/1, wait/1]).
+
+-export([init/1, handle_call/3, handle_cast/2,
+ handle_info/2, terminate/2, code_change/3]).
+
+-include_lib("eunit/include/eunit.hrl").
+
+-record(barrier, {
+ target,
+ counter,
+ timer
+ }).
+
+start(time, Target)
+ when is_tuple(Target) ->
+ gen_server:start(barrier, [time, Target], []);
+
+start(counter, Target)
+ when is_integer(Target) ->
+ gen_server:start(barrier, [counter, Target], []).
+
+init([time, Time])
+ when is_tuple(Time) ->
+ process_flag(trap_exit, true),
+ Delta = case Time of
+ {seconds, N} ->
+ timer:seconds(N);
+ {minutes, N} ->
+ timer:minutes(N)
+ end,
+ {ok, Timer} = timer:apply_after(Delta, barrier, stop, [self()]),
+ {ok, #barrier{timer = Timer}};
+
+init([counter, Target])
+ when is_integer(Target) ->
+ process_flag(trap_exit, true),
+ {ok, #barrier{target = Target, counter = 0}}.
+
+stop(Barrier)
+ when is_pid(Barrier) ->
+ gen_server:cast(Barrier, {stop, self()}).
+
+bump(Barrier) ->
+ gen_server:cast(Barrier, 'BUMP').
+
+terminate(_Reason, State)
+ when State#barrier.timer /= undefined ->
+ catch timer:cancel(State#barrier.timer),
+ ok;
+
+terminate(_Reason, _State) ->
+ ok.
+
+handle_cast('BUMP', State)
+ when State#barrier.counter + 1 >= State#barrier.target ->
+ {stop, normal, State};
+
+handle_cast('BUMP', State) ->
+ N = State#barrier.counter,
+ {noreply, State#barrier{ counter = N + 1}};
+
+handle_cast({'TARGET', N}, State) ->
+ {noreply, State#barrier{ target = N }};
+
+handle_cast({stop, _Pid}, State) ->
+ {stop, normal, State};
+
+handle_cast(Event, State) ->
+ {stop, {unknown_cast, Event}, State}.
+
+handle_call('COUNTER', _From, State) ->
+ {reply, State#barrier.counter, State};
+
+handle_call('TARGET', _From, State) ->
+ {reply, State#barrier.target, State};
+
+handle_call(Event, From, State) ->
+ {stop, {unknown_call, Event, From}, State}.
+
+handle_info({'EXIT', _Pid, _Reason}, State) ->
+ %% child exit?
+ {noreply, State};
+
+handle_info(Info, State) ->
+ {stop, {unknown_info, Info}, State}.
+
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+wait(Barrier) ->
+ TE = process_flag(trap_exit, true),
+ link(Barrier),
+ receive {'EXIT', Barrier, normal} -> ok end,
+ process_flag(trap_exit, TE),
+ ok.
+
+%%%
+%%% Test suite
+%%%
+
+counter_test() ->
+ {ok, B} = start(counter, 2),
+ ?assertEqual(true, util:is_process_alive(B)),
+ bump(B),
+ ?assertEqual(true, util:is_process_alive(B)),
+ bump(B),
+ timer:sleep(100),
+ ?assertEqual(false, util:is_process_alive(B)),
+ ok.
+
+timer_test() ->
+ {ok, B} = start(time, {seconds, 2}),
+ ?assertEqual(true, util:is_process_alive(B)),
+ timer:sleep(2000),
+ ?assertEqual(false, util:is_process_alive(B)),
+ ok.
+
+wait_test() ->
+ {ok, B} = start(time, {seconds, 2}),
+ ?assertEqual(ok, wait(B)),
+ ok.
+
@@ -0,0 +1,35 @@
+%%% Copyright (c) 2009 Oortle, Inc
+
+%%% Permission is hereby granted, free of charge, to any person
+%%% obtaining a copy of this software and associated documentation
+%%% files (the "Software"), to deal in the Software without restriction,
+%%% including without limitation the rights to use, copy, modify, merge,
+%%% publish, distribute, sublicense, and/or sell copies of the Software,
+%%% and to permit persons to whom the Software is furnished to do so,
+%%% subject to the following conditions:
+
+%%% The above copyright notice and this permission notice shall be included
+%%% in all copies or substantial portions of the Software.
+
+%%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+%%% OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+%%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+%%% THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+%%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+%%% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+%%% DEALINGS IN THE SOFTWARE.
+
+-module(bin).
+
+-export([split/2]).
+
+split(Sep, Bin)
+ when is_list(Sep),
+ is_binary(Bin) ->
+ case re:split(Bin, Sep, [{return, binary}, {parts, 2}]) of
+ [Bin1, Bin2] when size(Bin1) < size(Bin) andalso size(Bin2) < size(Bin) ->
+
+ {ok, Bin1, Bin2};
+ [Bin1] ->
+ {more, Bin1}
+ end.
Oops, something went wrong.

0 comments on commit e4ce25d

Please sign in to comment.