Skip to content

Commit

Permalink
Merge remote branch 'origin/new-parallelization-15366'
Browse files Browse the repository at this point in the history
  • Loading branch information
David Reid committed Jan 21, 2011
2 parents e4d1088 + 5c6eeb2 commit fc375ff
Show file tree
Hide file tree
Showing 5 changed files with 146 additions and 25 deletions.
2 changes: 2 additions & 0 deletions README
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
egeoip is a pure Erlang library for dealing with MaxMind Geolocation databases.

This product includes GeoLite data created by MaxMind, available from http://www.maxmind.com/.
Binary file added priv/GeoLiteCity.dat
Binary file not shown.
39 changes: 39 additions & 0 deletions priv/LICENSE.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
There are two licenses, one for the C library software, and one for
the database.

SOFTWARE LICENSE (C library)

The GeoIP C Library is licensed under the LGPL. For details see
the COPYING file.

OPEN DATA LICENSE (GeoLite Country and GeoLite City databases)

Copyright (c) 2008 MaxMind, Inc. All Rights Reserved.

All advertising materials and documentation mentioning features or use of
this database must display the following acknowledgment:
"This product includes GeoLite data created by MaxMind, available from
http://maxmind.com/"

Redistribution and use with or without modification, are permitted provided
that the following conditions are met:
1. Redistributions must retain the above copyright notice, this list of
conditions and the following disclaimer in the documentation and/or other
materials provided with the distribution.
2. All advertising materials and documentation mentioning features or use of
this database must display the following acknowledgement:
"This product includes GeoLite data created by MaxMind, available from
http://maxmind.com/"
3. "MaxMind" may not be used to endorse or promote products derived from this
database without specific prior written permission.

THIS DATABASE IS PROVIDED BY MAXMIND, INC ``AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL MAXMIND BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
DATABASE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
106 changes: 85 additions & 21 deletions src/egeoip.erl
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
-export([record_fields/0]).

%% gen_server based API
-export([start/0, start/1, stop/0, lookup/1, lookup_pl/1,
reload/0, reload/1, filename/0]).
-export([start/0, start/1, start_link/1, start_link/2, stop/0,
lookup/1, lookup_pl/1, reload/0, reload/1, filename/0]).

%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, terminate/2, code_change/3,
Expand Down Expand Up @@ -176,6 +176,7 @@
"Satellite Provider","Other","Aland Islands","Guernsey",
"Isle of Man","Jersey", "Saint Barthelemy","Saint Martin"}).


-record(geoipdb, {type = ?GEOIP_COUNTRY_EDITION,
record_length = ?STANDARD_RECORD_LENGTH,
segments = 0,
Expand Down Expand Up @@ -236,32 +237,62 @@ reload() ->
reload(FileName) ->
case new(FileName) of
{ok, NewState} ->
gen_server:call(?MODULE, {reload, NewState});
Workers = egeoip_sup:worker_names(),
[gen_server:call(W, {reload, NewState}) || W <- tuple_to_list(Workers)];
Error ->
Error
end.

%% @spec start() -> {ok, Pid}
%% @doc Start the server using the default priv/GeoLitecity.dat.gz database.
%% @spec start() -> ok
%% @doc Start the egeoip application with the default database.
start() ->
start(city).
application:start(egeoip).

%% @spec start(File) -> ok
%% @doc Start the egeoip application with the File as database.
start(File) ->
application:load(egeoip),
application:set_env(egeoip, dbfile, File),
start().


%% @spec start_link(Name) -> {ok, Pid}
%% @doc Start the server using the default priv/GeoLitecity.dat.gz database.
%% The process will be registered as Name
start_link(Name) ->
start_link(Name, city).

%% @spec start(Path) -> {ok, Pid}
%% @doc Start the server using the database at Path.
start(FileName) ->
%% @spec start_link(Name, Path) -> {ok, Pid}
%% @doc Start the server using the database at Path registered as Name.
start_link(Name, FileName) ->
gen_server:start_link(
{local, ?MODULE}, ?MODULE, FileName, []).
{local, Name}, ?MODULE, FileName, []).

%% @spec stop() -> ok
%% @doc Stop the server.
stop() ->
gen_server:cast(?MODULE, stop).
application:stop(egeoip).

%% @spec lookup(Address) -> geoip()
%% @doc Get a geoip() record for the given address. Fields can be obtained
%% from the record using get/2.
lookup(Address) ->
gen_server:call(?MODULE, {lookup, Address}).
case whereis(egeoip) of
undefined ->
Worker = get_worker(Address),
gen_server:call(Worker, {lookup, Address});
Pid ->
unregister(egeoip),
register(egeoip_0, Pid),
FileName = gen_server:call(Pid, filename),
[egeoip_0 | Workers] = tuple_to_list(egeoip_sup:worker_names()),
Specs = egeoip_sup:worker(Workers, FileName),
lists:map(fun(Spec) ->
{ok, _Pid} = supervisor:start_child(egeoip_sup, Spec)
end, Specs),
lookup(Address)
end.


%% @spec lookup_pl(Address) -> geoip()
%% @doc Get a proplist version of a geoip() record for the given address.
Expand All @@ -283,7 +314,7 @@ record_fields() ->
%% @spec filename() -> string()
%% @doc Get the database filename currently being used by the server.
filename() ->
gen_server:call(?MODULE, filename).
gen_server:call(element(1, egeoip_sup:worker_names()), filename).

%% gen_server callbacks

Expand Down Expand Up @@ -324,7 +355,10 @@ handle_info(Info, State) ->
{noreply, State}.

%% Implementation

get_worker(Address) ->
element(1 + erlang:phash2(Address) band 7,
egeoip_sup:worker_names()).

%% @spec new() -> {ok, geoipdb()}
%% @doc Create a new geoipdb database record using the default
%% priv/GeoLiteCity.dat.gz database.
Expand Down Expand Up @@ -630,11 +664,9 @@ bench(Count) ->
"61.16.226.206",
"64.180.1.78",
"138.217.4.11"],
{ok, _} = start(),
StartParse = now(),
benchcall(fun () -> [lookup(X) || X <- SampleIPs] end, Count),
EndParse = now(),
ok = stop(),
{parse_100k_addr, pytime(EndParse) - pytime(StartParse)}.

ensure_binary_list(L) when is_list(L) ->
Expand All @@ -651,16 +683,26 @@ bench() ->
-include_lib("eunit/include/eunit.hrl").
-ifdef(TEST).

egeoip_bench_test() ->
run_test_() ->
{inorder,
{foreach,
fun start/0,
fun(_) -> stop() end,
[fun egeoip_bench/0,
fun egeoip/0,
fun non_parallel/0
]
}}.

egeoip_bench() ->
?assertMatch(
{_, _},
bench(1)),
ok.

egeoip_test() ->
egeoip() ->
{ok, IpAddressLong} = ip2long({207,145,216,106}),
{ok, IpAddressLong} = ip2long("207.145.216.106"),
egeoip:start(),
{ok, R} = egeoip:lookup(IpAddressLong),
#geoip{country_code = "US",
country_code3 = "USA",
Expand All @@ -673,7 +715,29 @@ egeoip_test() ->
country_code3 = "USA",
country_name = "United States",
region = <<"NY">>,
_ = _} = R1,
ok.
_ = _} = R1.

non_parallel() ->
%% recreate the non-parallelized version of egeoip and then verify
%% that the upgrade works.
Workers = [Egeoip | T] = tuple_to_list(egeoip_sup:worker_names()),
%% Remove all worker processes except for the first one
lists:map(fun(Worker) ->
ok = supervisor:terminate_child(egeoip_sup, Worker),
ok = supervisor:delete_child(egeoip_sup, Worker)
end, T),
Pid = whereis(Egeoip),
unregister(Egeoip),
register(egeoip, Pid),
?assert(Pid == whereis(egeoip)),
[?assert(undefined == whereis(W)) || W <- Workers],
%% Should upgrade when calling lookup
{ok, _R} = egeoip:lookup("24.24.24.24"),
?assert(undefined == whereis(egeoip)),
[?assertNot(undefined == whereis(W)) || W <- Workers].

no_egeoip_test() ->
Lookup = {lookup, "24.24.24.24"},
?assertExit({noproc,{gen_server,call,[egeoip,Lookup]}},gen_server:call(egeoip, Lookup)).

-endif.
24 changes: 20 additions & 4 deletions src/egeoip_sup.erl
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

-export([start_link/0]).
-export([init/1]).
-export([worker/2, worker_names/0]).

start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
Expand All @@ -19,7 +20,22 @@ init([]) ->
_ ->
city
end,
Process = {egeoip,
{egeoip, start, [File]}, permanent, 5000, worker, [egeoip]},
{ok, {{one_for_one, 0, 300}, [Process]}}.

Processes = worker(tuple_to_list(worker_names()), File),
{ok, {{one_for_one, 5, 300}, Processes}}.

worker_names() ->
{egeoip_0,
egeoip_1,
egeoip_2,
egeoip_3,
egeoip_4,
egeoip_5,
egeoip_6,
egeoip_7}.

worker([], _File) ->
[];
worker([Name | T], File) ->
[{Name,
{egeoip, start_link, [Name, File]},
permanent, 5000, worker, [egeoip]} | worker(T, File)].

0 comments on commit fc375ff

Please sign in to comment.