You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by be...@apache.org on 2014/02/13 17:35:47 UTC

[14/23] goldrush commit: updated refs/heads/import-master to 71e6321

Protect statistics table and data from user errors


Project: http://git-wip-us.apache.org/repos/asf/couchdb-goldrush/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-goldrush/commit/81e3060c
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-goldrush/tree/81e3060c
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-goldrush/diff/81e3060c

Branch: refs/heads/import-master
Commit: 81e3060c53c07a2d9d78b1a7554be0c3d567a117
Parents: 879c698
Author: Pedram Nimreezi <de...@deadzen.com>
Authored: Thu Nov 7 02:13:23 2013 -0500
Committer: Pedram Nimreezi <de...@deadzen.com>
Committed: Thu Nov 7 02:21:18 2013 -0500

----------------------------------------------------------------------
 src/glc.erl            | 225 +++++++++++++++++++++++++-------------------
 src/glc_code.erl       |  11 +--
 src/goldrush.app.src   |   3 +-
 src/gr_app.erl         |  27 ++++++
 src/gr_counter.erl     | 161 +++++++++++++++++++++++++++++++
 src/gr_counter_mgr.erl | 170 +++++++++++++++++++++++++++++++++
 src/gr_sup.erl         |  30 ++++++
 src/gre.erl            |   2 +-
 8 files changed, 521 insertions(+), 108 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-goldrush/blob/81e3060c/src/glc.erl
----------------------------------------------------------------------
diff --git a/src/glc.erl b/src/glc.erl
index ecca4b7..1dc0928 100644
--- a/src/glc.erl
+++ b/src/glc.erl
@@ -200,12 +200,10 @@ module_data(Query) ->
     Params = ets:new(params, [set,protected]),
     %% query counters are stored in a shared ETS table. this should
     %% be an optional feature. enable by defaults to simplify tests.
-    Counters = ets:new(counters, [set,public]),
-    ets:insert(Counters, [{input,0}, {filter,0}, {output,0}]),
     %% the abstract_tables/1 function expects a list of name-tid pairs.
     %% tables are referred to by name in the generated code. the table/1
     %% function maps names to tids.
-    Tables = [{params,Params}, {counters,Counters}],
+    Tables = [{params,Params}],
     Query2 = glc_lib:reduce(Query),
     {ok, #module{'query'=Query, tables=Tables, qtree=Query2}}.
 
@@ -261,102 +259,131 @@ nullquery_exists_test() ->
     ?assertError(badarg, Mod:info(invalid)),
     ?assertEqual({null, false}, Mod:info('query')).
 
-init_counters_test() ->
-    {compiled, Mod} = setup_query(testmod4, glc:null(false)),
-    ?assertEqual(0, Mod:info(input)),
-    ?assertEqual(0, Mod:info(filter)),
-    ?assertEqual(0, Mod:info(output)).
-
-filtered_event_test() ->
-    %% If no selection condition is specified no inputs can match.
-    {compiled, Mod} = setup_query(testmod5, glc:null(false)),
-    glc:handle(Mod, gre:make([], [list])),
-    ?assertEqual(1, Mod:info(input)),
-    ?assertEqual(1, Mod:info(filter)),
-    ?assertEqual(0, Mod:info(output)).
-
-nomatch_event_test() ->
-    %% If a selection condition but no body is specified the event
-    %% is expected to count as filtered out if the condition does
-    %% not hold.
-    {compiled, Mod} = setup_query(testmod6, glc:eq('$n', 'noexists@nohost')),
-    glc:handle(Mod, gre:make([{'$n', 'noexists2@nohost'}], [list])),
-    ?assertEqual(1, Mod:info(input)),
-    ?assertEqual(1, Mod:info(filter)),
-    ?assertEqual(0, Mod:info(output)).
-
-opfilter_eq_test() ->
-    %% If a selection condition but no body is specified the event
-    %% counts as input to the query, but not as filtered out.
-    {compiled, Mod} = setup_query(testmod7, glc:eq('$n', 'noexists@nohost')),
-    glc:handle(Mod, gre:make([{'$n', 'noexists@nohost'}], [list])),
-    ?assertEqual(1, Mod:info(input)),
-    ?assertEqual(0, Mod:info(filter)),
-    ?assertEqual(1, Mod:info(output)),
-    done.
-
-
-opfilter_gt_test() ->
-    {compiled, Mod} = setup_query(testmod8, glc:gt(a, 1)),
-    glc:handle(Mod, gre:make([{'a', 2}], [list])),
-    ?assertEqual(1, Mod:info(input)),
-    ?assertEqual(0, Mod:info(filter)),
-    glc:handle(Mod, gre:make([{'a', 0}], [list])),
-    ?assertEqual(2, Mod:info(input)),
-    ?assertEqual(1, Mod:info(filter)),
-    ?assertEqual(1, Mod:info(output)),
-    done.
-
-opfilter_lt_test() ->
-    {compiled, Mod} = setup_query(testmod9, glc:lt(a, 1)),
-    glc:handle(Mod, gre:make([{'a', 0}], [list])),
-    ?assertEqual(1, Mod:info(input)),
-    ?assertEqual(0, Mod:info(filter)),
-    ?assertEqual(1, Mod:info(output)),
-    glc:handle(Mod, gre:make([{'a', 2}], [list])),
-    ?assertEqual(2, Mod:info(input)),
-    ?assertEqual(1, Mod:info(filter)),
-    ?assertEqual(1, Mod:info(output)),
-    done.
-
-allholds_op_test() ->
-    {compiled, Mod} = setup_query(testmod10,
-        glc:all([glc:eq(a, 1), glc:eq(b, 2)])),
-    glc:handle(Mod, gre:make([{'a', 1}], [list])),
-    glc:handle(Mod, gre:make([{'a', 2}], [list])),
-    ?assertEqual(2, Mod:info(input)),
-    ?assertEqual(2, Mod:info(filter)),
-    glc:handle(Mod, gre:make([{'b', 1}], [list])),
-    glc:handle(Mod, gre:make([{'b', 2}], [list])),
-    ?assertEqual(4, Mod:info(input)),
-    ?assertEqual(4, Mod:info(filter)),
-    glc:handle(Mod, gre:make([{'a', 1},{'b', 2}], [list])),
-    ?assertEqual(5, Mod:info(input)),
-    ?assertEqual(4, Mod:info(filter)),
-    ?assertEqual(1, Mod:info(output)),
-    done.
-
-anyholds_op_test() ->
-    {compiled, Mod} = setup_query(testmod11,
-        glc:any([glc:eq(a, 1), glc:eq(b, 2)])),
-    glc:handle(Mod, gre:make([{'a', 2}], [list])),
-    glc:handle(Mod, gre:make([{'b', 1}], [list])),
-    ?assertEqual(2, Mod:info(input)),
-    ?assertEqual(2, Mod:info(filter)),
-    glc:handle(Mod, gre:make([{'a', 1}], [list])),
-    glc:handle(Mod, gre:make([{'b', 2}], [list])),
-    ?assertEqual(4, Mod:info(input)),
-    ?assertEqual(2, Mod:info(filter)),
-    done.
-
-with_function_test() ->
-    Self = self(),
-    {compiled, Mod} = setup_query(testmod12,
-        glc:with(glc:eq(a, 1), fun(Event) -> Self ! gre:fetch(a, Event) end)),
-    glc:handle(Mod, gre:make([{a,1}], [list])),
-    ?assertEqual(1, Mod:info(output)),
-    ?assertEqual(1, receive Msg -> Msg after 0 -> notcalled end),
-    done.
+events_test_() ->
+    {foreach,
+        fun() ->
+                error_logger:tty(false),
+                application:start(syntax_tools),
+                application:start(compiler),
+                application:start(goldrush)
+        end,
+        fun(_) ->
+                application:stop(goldrush),
+                application:stop(compiler),
+                application:stop(syntax_tools),
+                error_logger:tty(true)
+        end,
+        [
+            {"init counters test",
+                fun() ->
+                    {compiled, Mod} = setup_query(testmod4, glc:null(false)),
+                    ?assertEqual(0, Mod:info(input)),
+                    ?assertEqual(0, Mod:info(filter)),
+                    ?assertEqual(0, Mod:info(output))
+                end
+            },
+            {"filtered events test",
+                fun() ->
+                    %% If no selection condition is specified no inputs can match.
+                    {compiled, Mod} = setup_query(testmod5, glc:null(false)),
+                    glc:handle(Mod, gre:make([], [list])),
+                    ?assertEqual(1, Mod:info(input)),
+                    ?assertEqual(1, Mod:info(filter)),
+                    ?assertEqual(0, Mod:info(output))
+                end
+            },
+            {"nomatch event test",
+                fun() ->
+                    %% If a selection condition but no body is specified the event
+                    %% is expected to count as filtered out if the condition does
+                    %% not hold.
+                    {compiled, Mod} = setup_query(testmod6, glc:eq('$n', 'noexists@nohost')),
+                    glc:handle(Mod, gre:make([{'$n', 'noexists2@nohost'}], [list])),
+                    ?assertEqual(1, Mod:info(input)),
+                    ?assertEqual(1, Mod:info(filter)),
+                    ?assertEqual(0, Mod:info(output))
+                end
+            },
+            {"opfilter equal test",
+                fun() ->
+                    %% If a selection condition but no body is specified the event
+                    %% counts as input to the query, but not as filtered out.
+                    {compiled, Mod} = setup_query(testmod7, glc:eq('$n', 'noexists@nohost')),
+                    glc:handle(Mod, gre:make([{'$n', 'noexists@nohost'}], [list])),
+                    ?assertEqual(1, Mod:info(input)),
+                    ?assertEqual(0, Mod:info(filter)),
+                    ?assertEqual(1, Mod:info(output))
+                end
+            },
+            {"opfilter greater than test",
+                fun() ->
+                    {compiled, Mod} = setup_query(testmod8, glc:gt(a, 1)),
+                    glc:handle(Mod, gre:make([{'a', 2}], [list])),
+                    ?assertEqual(1, Mod:info(input)),
+                    ?assertEqual(0, Mod:info(filter)),
+                    glc:handle(Mod, gre:make([{'a', 0}], [list])),
+                    ?assertEqual(2, Mod:info(input)),
+                    ?assertEqual(1, Mod:info(filter)),
+                    ?assertEqual(1, Mod:info(output))
+                end
+            },
+            {"opfilter less than test",
+                fun() ->
+                    {compiled, Mod} = setup_query(testmod9, glc:lt(a, 1)),
+                    glc:handle(Mod, gre:make([{'a', 0}], [list])),
+                    ?assertEqual(1, Mod:info(input)),
+                    ?assertEqual(0, Mod:info(filter)),
+                    ?assertEqual(1, Mod:info(output)),
+                    glc:handle(Mod, gre:make([{'a', 2}], [list])),
+                    ?assertEqual(2, Mod:info(input)),
+                    ?assertEqual(1, Mod:info(filter)),
+                    ?assertEqual(1, Mod:info(output))
+                end
+            },
+            {"allholds op test",
+                fun() ->
+                    {compiled, Mod} = setup_query(testmod10,
+                        glc:all([glc:eq(a, 1), glc:eq(b, 2)])),
+                    glc:handle(Mod, gre:make([{'a', 1}], [list])),
+                    glc:handle(Mod, gre:make([{'a', 2}], [list])),
+                    ?assertEqual(2, Mod:info(input)),
+                    ?assertEqual(2, Mod:info(filter)),
+                    glc:handle(Mod, gre:make([{'b', 1}], [list])),
+                    glc:handle(Mod, gre:make([{'b', 2}], [list])),
+                    ?assertEqual(4, Mod:info(input)),
+                    ?assertEqual(4, Mod:info(filter)),
+                    glc:handle(Mod, gre:make([{'a', 1},{'b', 2}], [list])),
+                    ?assertEqual(5, Mod:info(input)),
+                    ?assertEqual(4, Mod:info(filter)),
+                    ?assertEqual(1, Mod:info(output))
+                end
+            },
+            {"anyholds op test",
+                fun() ->
+                    {compiled, Mod} = setup_query(testmod11,
+                        glc:any([glc:eq(a, 1), glc:eq(b, 2)])),
+                    glc:handle(Mod, gre:make([{'a', 2}], [list])),
+                    glc:handle(Mod, gre:make([{'b', 1}], [list])),
+                    ?assertEqual(2, Mod:info(input)),
+                    ?assertEqual(2, Mod:info(filter)),
+                    glc:handle(Mod, gre:make([{'a', 1}], [list])),
+                    glc:handle(Mod, gre:make([{'b', 2}], [list])),
+                    ?assertEqual(4, Mod:info(input)),
+                    ?assertEqual(2, Mod:info(filter))
+                end
+            },
+            {"with function test",
+                fun() ->
+                    Self = self(),
+                    {compiled, Mod} = setup_query(testmod12,
+                        glc:with(glc:eq(a, 1), fun(Event) -> Self ! gre:fetch(a, Event) end)),
+                    glc:handle(Mod, gre:make([{a,1}], [list])),
+                    ?assertEqual(1, Mod:info(output)),
+                    ?assertEqual(1, receive Msg -> Msg after 0 -> notcalled end)
+                end
+            }
+        ]
+    }.
 
 union_error_test() ->
     ?assertError(badarg, glc:union([glc:eq(a, 1)])),

http://git-wip-us.apache.org/repos/asf/couchdb-goldrush/blob/81e3060c/src/glc_code.erl
----------------------------------------------------------------------
diff --git a/src/glc_code.erl b/src/glc_code.erl
index 810ca3c..1c20880 100644
--- a/src/glc_code.erl
+++ b/src/glc_code.erl
@@ -316,9 +316,8 @@ param_variable(Key) ->
 %% @todo Pass state record. Only Generate code if `statistics' is enabled.
 -spec abstract_count(atom()) -> syntaxTree().
 abstract_count(Counter) ->
-    abstract_apply(ets, update_counter,
-        [abstract_apply(table, [?erl:atom(counters)]),
-         ?erl:abstract(Counter),
+    abstract_apply(gr_counter, update,
+        [?erl:abstract(Counter),
          ?erl:abstract({2,1})]).
 
 
@@ -326,10 +325,8 @@ abstract_count(Counter) ->
 %% @todo Pass state record. Only Generate code if `statistics' is enabled.
 -spec abstract_getcount(atom()) -> [syntaxTree()].
 abstract_getcount(Counter) ->
-    [abstract_apply(ets, lookup_element,
-        [abstract_apply(table, [?erl:atom(counters)]),
-         ?erl:abstract(Counter),
-         ?erl:abstract(2)])].
+    [abstract_apply(gr_counter, check,
+        [?erl:abstract(Counter)])].
 
 
 %% abstract code util functions

http://git-wip-us.apache.org/repos/asf/couchdb-goldrush/blob/81e3060c/src/goldrush.app.src
----------------------------------------------------------------------
diff --git a/src/goldrush.app.src b/src/goldrush.app.src
index 6feac3c..4f482b4 100644
--- a/src/goldrush.app.src
+++ b/src/goldrush.app.src
@@ -1,7 +1,8 @@
 {application, goldrush, [
     {description, "Erlang event stream processor"},
-    {vsn, "0.1.2"},
+    {vsn, "0.1.3"},
     {registered, []},
     {applications, [kernel, stdlib, syntax_tools, compiler]},
+    {mod, {gr_app, []}},
     {env, []}
 ]}.

http://git-wip-us.apache.org/repos/asf/couchdb-goldrush/blob/81e3060c/src/gr_app.erl
----------------------------------------------------------------------
diff --git a/src/gr_app.erl b/src/gr_app.erl
new file mode 100644
index 0000000..20b3f9b
--- /dev/null
+++ b/src/gr_app.erl
@@ -0,0 +1,27 @@
+%% Copyright (c) 2012, Magnus Klaar <kl...@ninenines.eu>
+%%
+%% Permission to use, copy, modify, and/or distribute this software for any
+%% purpose with or without fee is hereby granted, provided that the above
+%% copyright notice and this permission notice appear in all copies.
+%%
+%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+-module(gr_app).
+-behaviour(application).
+
+-export([
+    start/2,
+    stop/1
+]).
+
+start(_Type, _Args) ->
+    gr_sup:start_link().
+
+stop(_State) ->
+    ok.

http://git-wip-us.apache.org/repos/asf/couchdb-goldrush/blob/81e3060c/src/gr_counter.erl
----------------------------------------------------------------------
diff --git a/src/gr_counter.erl b/src/gr_counter.erl
new file mode 100644
index 0000000..f11550b
--- /dev/null
+++ b/src/gr_counter.erl
@@ -0,0 +1,161 @@
+%% Copyright (c) 2013, Pedram Nimreezi <de...@deadzen.com>
+%%
+%% Permission to use, copy, modify, and/or distribute this software for any
+%% purpose with or without fee is hereby granted, provided that the above
+%% copyright notice and this permission notice appear in all copies.
+%%
+%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+-module(gr_counter).
+
+-behaviour(gen_server).
+
+%% API
+-export([start_link/0, 
+         check/0, check/1,
+         update/2]).
+
+%% gen_server callbacks
+-export([init/1,
+         handle_call/3,
+         handle_cast/2,
+         handle_info/2,
+         terminate/2,
+         code_change/3]).
+
+-define(SERVER, ?MODULE).
+
+-record(state, {init=true, table_id}).
+
+%%%===================================================================
+%%% API
+%%%===================================================================
+check() ->
+    gen_server:call(?MODULE, check).
+
+check(Counter) ->
+    gen_server:call(?MODULE, {check, Counter}).
+
+update(Counter, Value) ->
+    gen_server:cast(?MODULE, {update, Counter, Value}).
+
+%%--------------------------------------------------------------------
+%% @doc
+%% Starts the server
+%%
+%% @spec start_link() -> {ok, Pid} | ignore | {error, Error}
+%% @end
+%%--------------------------------------------------------------------
+start_link() ->
+    gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
+
+%%%===================================================================
+%%% gen_server callbacks
+%%%===================================================================
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Initializes the server
+%%
+%% @spec init(Args) -> {ok, State} |
+%%                     {ok, State, Timeout} |
+%%                     ignore |
+%%                     {stop, Reason}
+%% @end
+%%--------------------------------------------------------------------
+init([]) ->
+    {ok, #state{}}.
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Handling call messages
+%%
+%% @spec handle_call(Request, From, State) ->
+%%                                   {reply, Reply, State} |
+%%                                   {reply, Reply, State, Timeout} |
+%%                                   {noreply, State} |
+%%                                   {noreply, State, Timeout} |
+%%                                   {stop, Reason, Reply, State} |
+%%                                   {stop, Reason, State}
+%% @end
+%%--------------------------------------------------------------------
+handle_call(check, _From, State) ->
+    TableId = State#state.table_id,
+    {reply, {ok, ets:tab2list(TableId)}, State};
+handle_call({check, Counter}, _From, State) ->
+    TableId = State#state.table_id,
+    {reply, ets:lookup_element(TableId, Counter, 2), State};
+handle_call(_Request, _From, State) ->
+    Reply = ok,
+    {reply, Reply, State}.
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Handling cast messages
+%%
+%% @spec handle_cast(Msg, State) -> {noreply, State} |
+%%                                  {noreply, State, Timeout} |
+%%                                  {stop, Reason, State}
+%% @end
+%%--------------------------------------------------------------------
+handle_cast({update, Counter, Value}, State) ->
+    TableId = State#state.table_id,
+    ets:update_counter(TableId, Counter, Value),
+    {noreply, State};
+handle_cast(_Msg, State) ->
+    {noreply, State}.
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Handling all non call/cast messages
+%%
+%% @spec handle_info(Info, State) -> {noreply, State} |
+%%                                   {noreply, State, Timeout} |
+%%                                   {stop, Reason, State}
+%% @end
+%%--------------------------------------------------------------------
+handle_info({'ETS-TRANSFER', TableId, _Pid, _Data}, State) ->
+    {noreply, State#state{table_id=TableId}};
+handle_info(_Info, State) ->
+    {noreply, State}.
+
+%%--------------------------------------------------------------------
+%% @private
+%% @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.
+%%
+%% @spec terminate(Reason, State) -> void()
+%% @end
+%%--------------------------------------------------------------------
+terminate(_Reason, _State) ->
+    ok.
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Convert process state when code is changed
+%%
+%% @spec code_change(OldVsn, State, Extra) -> {ok, NewState}
+%% @end
+%%--------------------------------------------------------------------
+code_change(_OldVsn, State, _Extra) ->
+    {ok, State}.
+
+%%%===================================================================
+%%% Internal functions
+%%%===================================================================
+
+

http://git-wip-us.apache.org/repos/asf/couchdb-goldrush/blob/81e3060c/src/gr_counter_mgr.erl
----------------------------------------------------------------------
diff --git a/src/gr_counter_mgr.erl b/src/gr_counter_mgr.erl
new file mode 100644
index 0000000..5c9cabc
--- /dev/null
+++ b/src/gr_counter_mgr.erl
@@ -0,0 +1,170 @@
+%% Copyright (c) 2013, Pedram Nimreezi <de...@deadzen.com>
+%%
+%% Permission to use, copy, modify, and/or distribute this software for any
+%% purpose with or without fee is hereby granted, provided that the above
+%% copyright notice and this permission notice appear in all copies.
+%%
+%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+-module(gr_counter_mgr).
+
+-behaviour(gen_server).
+
+%% API
+-export([start_link/0]).
+
+%% gen_server callbacks
+-export([init/1,
+         handle_call/3,
+         handle_cast/2,
+         handle_info/2,
+         terminate/2,
+         code_change/3]).
+
+-define(SERVER, ?MODULE).
+
+-record(state, {table_id}).
+
+-define(CTR, gr_counter).
+
+%%%===================================================================
+%%% API
+%%%===================================================================
+
+setup(Data) ->
+    gen_server:cast(?MODULE, {setup, Data}).
+
+%%--------------------------------------------------------------------
+%% @doc
+%% Starts the server
+%%
+%% @spec start_link() -> {ok, Pid} | ignore | {error, Error}
+%% @end
+%%--------------------------------------------------------------------
+start_link() ->
+    gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
+
+%%%===================================================================
+%%% gen_server callbacks
+%%%===================================================================
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Initializes the server
+%%
+%% @spec init(Args) -> {ok, State} |
+%%                     {ok, State, Timeout} |
+%%                     ignore |
+%%                     {stop, Reason}
+%% @end
+%%--------------------------------------------------------------------
+init([]) ->
+    process_flag(trap_exit, true),
+    setup([{input,0}, {filter,0}, {output,0}]),
+    {ok, #state{}}.
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Handling call messages
+%%
+%% @spec handle_call(Request, From, State) ->
+%%                                   {reply, Reply, State} |
+%%                                   {reply, Reply, State, Timeout} |
+%%                                   {noreply, State} |
+%%                                   {noreply, State, Timeout} |
+%%                                   {stop, Reason, Reply, State} |
+%%                                   {stop, Reason, State}
+%% @end
+%%--------------------------------------------------------------------
+handle_call(_Request, _From, State) ->
+    Reply = ok,
+    {reply, Reply, State}.
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Handling cast messages
+%%
+%% @spec handle_cast(Msg, State) -> {noreply, State} |
+%%                                  {noreply, State, Timeout} |
+%%                                  {stop, Reason, State}
+%% @end
+%%--------------------------------------------------------------------
+handle_cast({setup, Data}, State) ->
+    Ctr = whereis(?CTR),
+    link(Ctr),
+    TableId = ets:new(?MODULE, [set, private]),
+    ets:insert(TableId, Data),
+    ets:setopts(TableId, {heir, self(), Data}),
+    ets:give_away(TableId, Ctr, Data),
+    {noreply, State#state{table_id=TableId}};
+handle_cast(_Msg, State) ->
+    {noreply, State}.
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Handling all non call/cast messages
+%%
+%% @spec handle_info(Info, State) -> {noreply, State} |
+%%                                   {noreply, State, Timeout} |
+%%                                   {stop, Reason, State}
+%% @end
+%%--------------------------------------------------------------------
+handle_info({'EXIT', _Pid, killed}, State) ->
+    {noreply, State};
+handle_info({'ETS-TRANSFER', TableId, _Pid, Data}, State) ->
+    Ctr = wait_for_ctr(),
+    link(Ctr),
+    ets:give_away(TableId, Ctr, Data),
+    {noreply, State#state{table_id=TableId}}.
+
+wait_for_ctr() -> 
+    case whereis(?CTR) of
+        undefined -> 
+            timer:sleep(1),
+            wait_for_ctr();
+        Pid -> Pid
+    end.
+
+
+
+%%--------------------------------------------------------------------
+%% @private
+%% @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.
+%%
+%% @spec terminate(Reason, State) -> void()
+%% @end
+%%--------------------------------------------------------------------
+terminate(_Reason, _State) ->
+    ok.
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Convert process state when code is changed
+%%
+%% @spec code_change(OldVsn, State, Extra) -> {ok, NewState}
+%% @end
+%%--------------------------------------------------------------------
+code_change(_OldVsn, State, _Extra) ->
+    {ok, State}.
+
+%%%===================================================================
+%%% Internal functions
+%%%===================================================================
+
+
+

http://git-wip-us.apache.org/repos/asf/couchdb-goldrush/blob/81e3060c/src/gr_sup.erl
----------------------------------------------------------------------
diff --git a/src/gr_sup.erl b/src/gr_sup.erl
new file mode 100644
index 0000000..b207d4c
--- /dev/null
+++ b/src/gr_sup.erl
@@ -0,0 +1,30 @@
+%% Copyright (c) 2012, Magnus Klaar <kl...@ninenines.eu>
+%%
+%% Permission to use, copy, modify, and/or distribute this software for any
+%% purpose with or without fee is hereby granted, provided that the above
+%% copyright notice and this permission notice appear in all copies.
+%%
+%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+%% @doc Top level supervisor for goldrush.
+-module(gr_sup).
+-behaviour(supervisor).
+
+-export([start_link/0]).
+-export([init/1]).
+
+-define(CHILD(I, Type), {I, {I, start_link, []}, permanent, 5000, Type, [I]}).
+
+start_link() ->
+    supervisor:start_link({local, ?MODULE}, ?MODULE, []).
+
+init([]) ->
+    Counter = ?CHILD(gr_counter, worker),
+    CounterMgr = ?CHILD(gr_counter_mgr, worker),
+    {ok, {{one_for_one, 5, 10}, [Counter, CounterMgr]}}.

http://git-wip-us.apache.org/repos/asf/couchdb-goldrush/blob/81e3060c/src/gre.erl
----------------------------------------------------------------------
diff --git a/src/gre.erl b/src/gre.erl
index eca4e2c..bea1981 100644
--- a/src/gre.erl
+++ b/src/gre.erl
@@ -25,7 +25,7 @@
 ]).
 
 -type event() :: {list, [{atom(), term()}]}.
--export_types([event/0]).
+-export_type([event/0]).
 
 %% @doc Construct an event term.
 -spec make(term(), [list]) -> event().