Skip to content
Browse files

Initial commit

  • Loading branch information...
0 parents commit 6d3a44da87d10d61d4bfd04bc3a1e97c9cb12e8c @jbrisbin committed Nov 14, 2011
5 .gitignore
@@ -0,0 +1,5 @@
+data
+deps
+ebin
+log
+.DS_Store
33 Makefile
@@ -0,0 +1,33 @@
+PACKAGE=misultin-riak-core-vnode-dispatcher
+DIST_DIR=dist
+EBIN_DIR=ebin
+INCLUDE_DIRS=include
+DEPS_DIR=deps
+DEPS ?= misultin basho_stats lager mochiweb poolboy protobuffs riak_core riak_sysmon webmachine
+DEPS_EZ=$(foreach DEP, $(DEPS), $(DEPS_DIR)/$(DEP).ez)
+RABBITMQ_HOME ?= .
+
+all: compile
+
+clean:
+ rm -rf $(DIST_DIR)
+ rm -rf $(EBIN_DIR)
+
+distclean: clean
+ rm -rf $(DEPS_DIR)
+
+package: compile $(DEPS_EZ)
+ rm -f $(DIST_DIR)/$(PACKAGE).ez
+ mkdir -p $(DIST_DIR)/$(PACKAGE)
+ cp -r $(EBIN_DIR) $(DIST_DIR)/$(PACKAGE)
+ $(foreach EXTRA_DIR, $(INCLUDE_DIRS), cp -r $(EXTRA_DIR) $(DIST_DIR)/$(PACKAGE);)
+ (cd $(DIST_DIR); zip -r $(PACKAGE).ez $(PACKAGE))
+
+$(DEPS_DIR):
+ ./rebar get-deps
+
+$(DEPS_EZ):
+ cd $(DEPS_DIR); $(foreach DEP, $(DEPS), zip -r $(DEP).ez $(DEP);)
+
+compile: $(DEPS_DIR)
+ ./rebar compile
50 README.md
@@ -0,0 +1,50 @@
+# Misultin Riak Core VNode Dispatcher
+
+This isn't so much a reusable application as an example of how to dispatch a web request
+into a riak_core vnode.
+
+There is a vnode called `hello_world_vnode` that responds with a misultin response. Right
+now it's:
+
+ Req:ok([{"Content-Type", "text/plain"}], <<"Hello World!">>)
+
+It dispatches into your vnode based on a tuple of `{Method, Path, Req}`. To respond to misultin
+requests in your vnode, you'll want to implement `handle_command` functions:
+
+ %% To handle root path: '/'
+ handle_command({'GET', [], Req}, _Sender, State) ->
+ Response = Req:ok([{"Content-Type", "text/plain"}], <<"Hello World!">>),
+ {reply, {ok, Response}, State};
+
+ %% To handle '/services/myservice' using path matching
+ handle_command({'GET', ["services", Svc], Req}, _Sender, State) ->
+ ...
+
+If you return a `{reply, {ok, Response}, State}`, the dispatcher will send that back on to the client.
+Although a `{noreply, State}` response is valid as far as riak_core is concerned, not sending a
+response back to the client at all will result in an eventual timeout. That's probably a Bad Thing
+in general. There is an HTTP status code for doing long-running, delayed processing if you need that
+sort of functionality. It's code 202 - Accepted.
+
+### How do I use it?
+
+That depends on your use case. I would start with your own riak_core application that
+already has your own vnodes configured and started in it. You would then copy out the
+`hello_world_vnode_dispatcher.erl` file and put that into your application (it's a gen_server).
+You'll want to edit the the `loop` function to maybe parameterize the vnode you dispatch to.
+Maybe you want to make that based on mapping a query parameter or path segment to a vnode
+name (I wouldn't use direct vnode names as parameters in your web app as that would expose
+important information about the internal structure of your site to a potential attacker).
+
+You'll also likely want to parameterize the port your app starts on. I'm assuming you're a savvy-
+enough OTP-nician to figure out how to do that. This isn't a HOWTO so much as example code
+showing you how I did it. Grab what you need, throw away the rest! :)
+
+### Running this code
+
+To test out this code, build it by typing `make` then running the `./console.sh` script, which
+will start the Erlang console with the right parameters. Once started, send a web request.
+
+ curl -v http://localhost:3000/
+
+You should get "Hello World!" back.
2 console.sh
@@ -0,0 +1,2 @@
+#!/bin/bash
+erl -pa ebin -pa deps/*/ebin -s hello_world -name helloworld@localhost
7 include/hello_world_vnode_dispatcher.hrl
@@ -0,0 +1,7 @@
+-ifndef(MISULTIN_RIAK_CORE_VNODE_DISPATCHER).
+-define(MISULTIN_RIAK_CORE_VNODE_DISPATCHER, ok).
+
+-define(VNODE_MASTER(Name, VNode), {Name, {riak_core_vnode_master, start_link, [VNode]}, permanent, 5000, worker, [riak_core_vnode_master]}).
+-define(GEN_SERVER(Name, Worker), {Name, {Worker, start_link, []}, permanent, 5000, worker, [Worker]}).
+
+-endif.
BIN rebar
Binary file not shown.
10 rebar.config
@@ -0,0 +1,10 @@
+{cover_enabled, true}.
+{erl_opts, [
+ debug_info
+ %fail_on_warning
+]}.
+
+{deps, [
+ {misultin, ".*", {git, "https://github.com/ostinelli/misultin.git", {tag, "misultin-0.8"}}},
+ {riak_core, ".*", {git, "https://github.com/basho/riak_core.git", {tag, "1.0.1"}}}
+]}.
12 src/hello_world.app.src
@@ -0,0 +1,12 @@
+{application, hello_world, [
+ {description, "Hello World! Test App"},
+ {vsn, "1.0"},
+ {registered, []},
+ {applications, [
+ kernel,
+ stdlib,
+ riak_core
+ ]},
+ {mod, { hello_world_app, []}},
+ {env, []}
+]}.
20 src/hello_world.erl
@@ -0,0 +1,20 @@
+-module(hello_world).
+
+-export([
+ start/0,
+ stop/0
+]).
+
+start() ->
+ net_kernel:start(['helloworld@localhost']),
+ application:start(crypto),
+ application:start(sasl),
+ application:start(lager),
+ application:start(webmachine),
+ application:start(os_mon),
+ application:start(riak_sysmon),
+ application:start(riak_core),
+ application:start(hello_world).
+
+stop() ->
+ ok.
20 src/hello_world_app.erl
@@ -0,0 +1,20 @@
+-module(hello_world_app).
+-behaviour(application).
+
+-export([
+ start/2,
+ stop/1
+]).
+
+start(_StartType, _StartArgs) ->
+ case hello_world_sup:start_link() of
+ {ok, Pid} ->
+ ok = riak_core:register_vnode_module(hello_world_vnode),
+ ok = riak_core_node_watcher:service_up(hello_world, self()),
+
+ {ok, Pid};
+ Else -> Else
+ end.
+
+stop(_State) ->
+ ok.
20 src/hello_world_sup.erl
@@ -0,0 +1,20 @@
+-module(hello_world_sup).
+-behaviour(supervisor).
+
+-include("hello_world_vnode_dispatcher.hrl").
+
+-export([
+ start_link/0,
+ init/1
+]).
+
+start_link() ->
+ supervisor:start_link({local, ?MODULE}, ?MODULE, []).
+
+init(_Args) ->
+ Workers = [
+ ?GEN_SERVER(hello_world_vnode_dispatcher_master, hello_world_vnode_dispatcher),
+ ?VNODE_MASTER(hello_world_vnode_master, hello_world_vnode)
+ ],
+
+ {ok, {{one_for_one, 10, 10}, Workers}}.
77 src/hello_world_vnode.erl
@@ -0,0 +1,77 @@
+-module(hello_world_vnode).
+-behaviour(riak_core_vnode).
+
+-include_lib("riak_core/include/riak_core_vnode.hrl").
+-include_lib("misultin/include/misultin.hrl").
+
+-export([
+ delete/1,
+ encode_handoff_item/2,
+ handle_command/3,
+ handle_coverage/4,
+ handle_exit/3,
+ handle_handoff_command/3,
+ handle_handoff_data/2,
+ handoff_cancelled/1,
+ handoff_finished/2,
+ handoff_starting/2,
+ init/1,
+ is_empty/1,
+ start_vnode/1,
+ terminate/2
+]).
+
+-record(state, {
+ partition :: partition()
+}).
+
+start_vnode(I) ->
+ io:format("starting ~p~n", [I]),
+ riak_core_vnode_master:get_vnode_pid(I, ?MODULE).
+
+init([Partition]) ->
+ io:format("started hello world vnode at ~p~n", [Partition]),
+ {ok, #state { partition = Partition }}.
+
+handle_command({'GET', [], Req}, _Sender, State) ->
+ io:format("from misultin: ~p~n", [Req]),
+ Response = Req:ok([{"Content-Type", "text/plain"}], <<"Hello World!">>),
+ {reply, {ok, Response}, State};
+
+handle_command(Msg, _Sender, State) ->
+ io:format("unhandled command: ~p~n", [Msg]),
+ {noreply, State}.
+
+handle_coverage(Request, KeySpaces, Sender, State) ->
+ io:format("handle_coverage: ~p ~p ~p ~p~n", [Request, KeySpaces, Sender, State]),
+ {continue, State}.
+
+handle_exit(_Pid, Reason, State) ->
+ {stop, Reason, State}.
+
+handle_handoff_command(_Message, _Sender, State) ->
+ {forward, State}.
+
+handoff_starting(_TargetNode, State) ->
+ {true, State}.
+
+handoff_cancelled(State) ->
+ {ok, State}.
+
+handoff_finished(_TargetNode, State) ->
+ {ok, State}.
+
+handle_handoff_data(_Data, State) ->
+ {reply, ok, State}.
+
+encode_handoff_item(_ObjectName, _ObjectValue) ->
+ <<>>.
+
+is_empty(State) ->
+ {true, State}.
+
+delete(State) ->
+ {ok, State}.
+
+terminate(_Reason, _State) ->
+ ok.
65 src/hello_world_vnode_dispatcher.erl
@@ -0,0 +1,65 @@
+-module(hello_world_vnode_dispatcher).
+-behaviour(gen_server2).
+
+-include("hello_world_vnode_dispatcher.hrl").
+-include_lib("misultin/include/misultin.hrl").
+
+-export([
+ start_link/0,
+ init/1,
+ handle_call/3,
+ handle_cast/2,
+ handle_info/2,
+ terminate/2,
+ code_change/3
+]).
+
+-record(state, { site, server, config }).
+
+start_link() ->
+ gen_server2:start_link({local, ?MODULE}, ?MODULE, [], [{timeout, infinity}]).
+
+init([]) ->
+ process_flag(trap_exit, true),
+
+ {ok, ServerPid} = misultin:start_link([{port, 3000}, {loop, fun(Req) ->
+
+ Method = Req:get(method),
+ Resource = Req:resource([lowercase, urldecode]),
+
+ Hash = riak_core_util:chash_key({Method, Resource}),
+ case riak_core_apl:get_primary_apl(Hash, 1, hello_world) of
+ [{Index, _Type}] ->
+ case riak_core_vnode_master:sync_spawn_command(Index, {Method, Resource, Req}, hello_world_vnode_master) of
+ {ok, Resp} ->
+ io:format("response: ~p~n", [Resp]),
+ Resp;
+ {error, Reason} -> Req:respond(500, Reason)
+ end;
+ {error, Reason} -> Req:respond(500, Reason)
+ end
+
+ end}]),
+
+
+ {ok, #state { server = ServerPid }}.
+
+handle_call(Msg, From, State) ->
+ io:format("handle_call: ~p ~p ~p~n", [Msg, From, State]),
+ {noreply, State}.
+
+handle_cast(Msg, State) ->
+ io:format("handle_cast: ~p ~p~n", [Msg, State]),
+ {noreply, State}.
+
+handle_info(Msg, State) ->
+ io:format("handle_info: ~p ~p~n", [Msg, State]),
+ {noreply, State}.
+
+terminate(Reason, State) ->
+ io:format("terminate: ~p ~p~n", [Reason, State]),
+ ok.
+
+code_change(_OldVsn, State, _Extra) ->
+ % io:format("code_change: ~p ~p ~p~n", [OldVsn, State, Extra]),
+ {ok, State}.

0 comments on commit 6d3a44d

Please sign in to comment.
Something went wrong with that request. Please try again.