diff --git a/ChangeLog b/ChangeLog index 89bc96da6..2765358e2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,15 @@ +2007-01-19 Alexey Shchepin + + * src/mod_muc/mod_muc.erl: Now mod_muc can be distributed on + several nodes + * src/mod_muc/mod_muc_room.erl: Likewise + + * src/ejabberd_router.erl: Added bare_source and bare_destination + service balancing options and domain_balancing_component_number + option for specifying the number of connected components for the + domain + * src/ejabberd_config.erl: Likewise + 2007-01-11 Mickael Remond * doc/guide.tex: Latex / Hevea related improvements for documentation diff --git a/src/ejabberd_config.erl b/src/ejabberd_config.erl index 257cb691a..199f374f3 100644 --- a/src/ejabberd_config.erl +++ b/src/ejabberd_config.erl @@ -121,9 +121,11 @@ process_term(Term, State) -> add_option(cluster_nodes, Nodes, State); {domain_balancing, Domain, Balancing} -> add_option({domain_balancing, Domain}, Balancing, State); - {loglevel, Loglevel} -> - ejabberd_loglevel:set(Loglevel), - State; + {domain_balancing_component_number, Domain, N} -> + add_option({domain_balancing_component_number, Domain}, N, State); + {loglevel, Loglevel} -> + ejabberd_loglevel:set(Loglevel), + State; {Opt, Val} -> lists:foldl(fun(Host, S) -> process_host_term(Term, Host, S) end, State, State#state.hosts) diff --git a/src/ejabberd_router.erl b/src/ejabberd_router.erl index 35c8d621c..af2e83f02 100644 --- a/src/ejabberd_router.erl +++ b/src/ejabberd_router.erl @@ -56,30 +56,54 @@ route(From, To, Packet) -> end. register_route(Domain) -> - case jlib:nameprep(Domain) of - error -> - [] = {invalid_domain, Domain}; - LDomain -> - Pid = self(), - F = fun() -> - mnesia:write(#route{domain = LDomain, - pid = Pid}) - end, - mnesia:transaction(F) - end. + register_route(Domain, undefined). register_route(Domain, LocalHint) -> case jlib:nameprep(Domain) of error -> - [] = {invalid_domain, Domain}; + erlang:error({invalid_domain, Domain}); LDomain -> Pid = self(), - F = fun() -> - mnesia:write(#route{domain = LDomain, - pid = Pid, - local_hint = LocalHint}) - end, - mnesia:transaction(F) + case get_component_number(LDomain) of + undefined -> + F = fun() -> + mnesia:write(#route{domain = LDomain, + pid = Pid, + local_hint = LocalHint}) + end, + mnesia:transaction(F); + N -> + F = fun() -> + case mnesia:read({route, LDomain}) of + [] -> + mnesia:write( + #route{domain = LDomain, + pid = Pid, + local_hint = 1}), + lists:foreach( + fun(I) -> + mnesia:write( + #route{domain = LDomain, + pid = undefined, + local_hint = I}) + end, lists:seq(2, N)); + Rs -> + lists:any( + fun(#route{pid = undefined, + local_hint = I} = R) -> + mnesia:write( + #route{domain = LDomain, + pid = Pid, + local_hint = I}), + mnesia:delete_object(R), + true; + (_) -> + false + end, Rs) + end + end, + mnesia:transaction(F) + end end. register_routes(Domains) -> @@ -90,14 +114,40 @@ register_routes(Domains) -> unregister_route(Domain) -> case jlib:nameprep(Domain) of error -> - [] = {invalid_domain, Domain}; + erlang:error({invalid_domain, Domain}); LDomain -> Pid = self(), - F = fun() -> - mnesia:delete_object(#route{domain = LDomain, - pid = Pid}) - end, - mnesia:transaction(F) + case get_component_number(LDomain) of + undefined -> + F = fun() -> + case mnesia:match(#route{domain = LDomain, + pid = Pid, + _ = '_'}) of + [R] -> + mnesia:delete_object(R); + _ -> + ok + end + end, + mnesia:transaction(F); + _ -> + F = fun() -> + case mnesia:match(#route{domain = LDomain, + pid = Pid, + _ = '_'}) of + [R] -> + I = R#route.local_hint, + mnesia:write( + #route{domain = LDomain, + pid = undefined, + local_hint = I}), + mnesia:delete_object(R); + _ -> + ok + end + end, + mnesia:transaction(F) + end end. unregister_routes(Domains) -> @@ -188,9 +238,21 @@ handle_info({'DOWN', _Ref, _Type, Pid, _Info}, State) -> [{#route{pid = Pid, _ = '_'}, [], ['$_']}]), - lists:foreach(fun(E) -> - mnesia:delete_object(E) - end, Es) + lists:foreach( + fun(E) -> + if + is_integer(E#route.local_hint) -> + LDomain = E#route.domain, + I = E#route.local_hint, + mnesia:write( + #route{domain = LDomain, + pid = undefined, + local_hint = I}), + mnesia:delete_object(E); + true -> + mnesia:delete_object(E) + end + end, Es) end, mnesia:transaction(F), {noreply, State}; @@ -237,37 +299,56 @@ do_route(OrigFrom, OrigTo, OrigPacket) -> _ -> Pid ! {route, From, To, Packet} end; + is_pid(Pid) -> + Pid ! {route, From, To, Packet}; true -> - Pid ! {route, From, To, Packet} + drop end; Rs -> - case [R || R <- Rs, node(R#route.pid) == node()] of - [] -> - Value = case ejabberd_config:get_local_option( - {domain_balancing, LDstDomain}) of - source -> jlib:jid_tolower(From); - destination -> jlib:jid_tolower(To); - random -> now(); - undefined -> now() - end, - R = lists:nth(erlang:phash(Value, length(Rs)), Rs), + Value = case ejabberd_config:get_local_option( + {domain_balancing, LDstDomain}) of + undefined -> now(); + random -> now(); + source -> jlib:jid_tolower(From); + destination -> jlib:jid_tolower(To); + bare_source -> + jlib:jid_remove_resource( + jlib:jid_tolower(From)); + bare_destination -> + jlib:jid_remove_resource( + jlib:jid_tolower(To)) + end, + case get_component_number(LDstDomain) of + undefined -> + case [R || R <- Rs, node(R#route.pid) == node()] of + [] -> + R = lists:nth(erlang:phash(Value, length(Rs)), Rs), + Pid = R#route.pid, + if + is_pid(Pid) -> + Pid ! {route, From, To, Packet}; + true -> + drop + end; + LRs -> + R = lists:nth(erlang:phash(Value, length(LRs)), LRs), + Pid = R#route.pid, + case R#route.local_hint of + {apply, Module, Function} -> + Module:Function(From, To, Packet); + _ -> + Pid ! {route, From, To, Packet} + end + end; + _ -> + SRs = lists:ukeysort(#route.local_hint, Rs), + R = lists:nth(erlang:phash(Value, length(SRs)), SRs), Pid = R#route.pid, - Pid ! {route, From, To, Packet}; - LRs -> - Value = case ejabberd_config:get_local_option( - {domain_balancing, LDstDomain}) of - source -> jlib:jid_tolower(From); - destination -> jlib:jid_tolower(To); - random -> now(); - undefined -> now() - end, - R = lists:nth(erlang:phash(Value, length(LRs)), LRs), - Pid = R#route.pid, - case R#route.local_hint of - {apply, Module, Function} -> - Module:Function(From, To, Packet); - _ -> - Pid ! {route, From, To, Packet} + if + is_pid(Pid) -> + Pid ! {route, From, To, Packet}; + true -> + drop end end end; @@ -275,7 +356,15 @@ do_route(OrigFrom, OrigTo, OrigPacket) -> ok end. - +get_component_number(LDomain) -> + case ejabberd_config:get_local_option( + {domain_balancing_component_number, LDomain}) of + N when is_integer(N), + N > 1 -> + N; + _ -> + undefined + end. update_tables() -> case catch mnesia:table_info(route, attributes) of diff --git a/src/mod_muc/mod_muc.erl b/src/mod_muc/mod_muc.erl index d5ecb5b91..9a8eadbac 100644 --- a/src/mod_muc/mod_muc.erl +++ b/src/mod_muc/mod_muc.erl @@ -17,7 +17,7 @@ -export([start_link/2, start/2, stop/1, - room_destroyed/3, + room_destroyed/4, store_room/3, restore_room/2, forget_room/2, @@ -69,9 +69,9 @@ stop(Host) -> gen_server:call(Proc, stop), supervisor:delete_child(ejabberd_sup, Proc). -room_destroyed(Host, Room, ServerHost) -> +room_destroyed(Host, Room, Pid, ServerHost) -> gen_mod:get_module_proc(ServerHost, ?PROCNAME) ! - {room_destroyed, {Room, Host}}, + {room_destroyed, {Room, Host}, Pid}, ok. store_room(Host, Name, Opts) -> @@ -142,6 +142,10 @@ init([Host, Opts]) -> mnesia:create_table(muc_registered, [{disc_copies, [node()]}, {attributes, record_info(fields, muc_registered)}]), + mnesia:create_table(muc_online_room, + [{ram_copies, [node()]}, + {attributes, record_info(fields, muc_online_room)}]), + mnesia:add_table_copy(muc_online_room, node(), ram_copies), MyHost = gen_mod:get_opt(host, Opts, "conference." ++ Host), update_tables(MyHost), mnesia:add_table_index(muc_registered, nick), @@ -149,9 +153,6 @@ init([Host, Opts]) -> AccessCreate = gen_mod:get_opt(access_create, Opts, all), AccessAdmin = gen_mod:get_opt(access_admin, Opts, none), HistorySize = gen_mod:get_opt(history_size, Opts, 20), - catch ets:new(muc_online_room, [named_table, - public, - {keypos, #muc_online_room.name_host}]), ejabberd_router:register_route(MyHost), load_permanent_rooms(MyHost, Host, {Access, AccessCreate, AccessAdmin}, HistorySize), @@ -199,8 +200,12 @@ handle_info({route, From, To, Packet}, ok end, {noreply, State}; -handle_info({room_destroyed, RoomHost}, State) -> - ets:delete(muc_online_room, RoomHost), +handle_info({room_destroyed, RoomHost, Pid}, State) -> + F = fun() -> + mnesia:delete_object(#muc_online_room{name_host = RoomHost, + pid = Pid}) + end, + mnesia:transaction(F), {noreply, State}; handle_info(_Info, State) -> {noreply, State}. @@ -373,7 +378,7 @@ do_route1(Host, ServerHost, Access, HistorySize, From, To, Packet) -> end end; _ -> - case ets:lookup(muc_online_room, {Room, Host}) of + case mnesia:dirty_read(muc_online_room, {Room, Host}) of [] -> Type = xml:get_attr_s("type", Attrs), case {Name, Type} of @@ -385,10 +390,7 @@ do_route1(Host, ServerHost, Access, HistorySize, From, To, Packet) -> Host, ServerHost, Access, Room, HistorySize, From, Nick), - ets:insert( - muc_online_room, - #muc_online_room{name_host = {Room, Host}, - pid = Pid}), + register_room(Host, Room, Pid), mod_muc_room:route(Pid, From, Nick, Packet), ok; _ -> @@ -425,22 +427,32 @@ load_permanent_rooms(Host, ServerHost, Access, HistorySize) -> ?ERROR_MSG("~p", [Reason]), ok; Rs -> - lists:foreach(fun(R) -> - {Room, Host} = R#muc_room.name_host, - {ok, Pid} = mod_muc_room:start( - Host, - ServerHost, - Access, - Room, - HistorySize, - R#muc_room.opts), - ets:insert( - muc_online_room, - #muc_online_room{name_host = {Room, Host}, - pid = Pid}) - end, Rs) + lists:foreach( + fun(R) -> + {Room, Host} = R#muc_room.name_host, + case mnesia:dirty_read(muc_online_room, {Room, Host}) of + [] -> + {ok, Pid} = mod_muc_room:start( + Host, + ServerHost, + Access, + Room, + HistorySize, + R#muc_room.opts), + register_room(Host, Room, Pid); + _ -> + ok + end + end, Rs) end. +register_room(Host, Room, Pid) -> + F = fun() -> + mnesia:write(#muc_online_room{name_host = {Room, Host}, + pid = Pid}) + end, + mnesia:transaction(F). + iq_disco_info() -> [{xmlelement, "identity", @@ -598,10 +610,10 @@ broadcast_service_message(Host, Msg) -> end, get_vh_rooms(Host)). get_vh_rooms(Host) -> - ets:select(muc_online_room, - [{#muc_online_room{name_host = '$1', _ = '_'}, - [{'==', {element, 2, '$1'}, Host}], - ['$_']}]). + mnesia:dirty_select(muc_online_room, + [{#muc_online_room{name_host = '$1', _ = '_'}, + [{'==', {element, 2, '$1'}, Host}], + ['$_']}]). diff --git a/src/mod_muc/mod_muc_room.erl b/src/mod_muc/mod_muc_room.erl index 2c9f268a6..e2f7d190d 100644 --- a/src/mod_muc/mod_muc_room.erl +++ b/src/mod_muc/mod_muc_room.erl @@ -677,7 +677,7 @@ handle_info(_Info, StateName, StateData) -> %% Returns: any %%---------------------------------------------------------------------- terminate(_Reason, _StateName, StateData) -> - mod_muc:room_destroyed(StateData#state.host, StateData#state.room, + mod_muc:room_destroyed(StateData#state.host, StateData#state.room, self(), StateData#state.server_host), ok.