From 592ed0b4013038d4fc65d242c72002e72501e8f4 Mon Sep 17 00:00:00 2001 From: SisMaker <156736github> Date: Wed, 13 Apr 2022 00:01:42 +0800 Subject: [PATCH] =?UTF-8?q?ft:=20=E5=AE=8C=E5=96=84=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/etsmatch.hrl.in | 21 ++++++++++ include/ranks.hrl | 2 + src/rank/rankMgr.erl | 2 +- src/rank/rankTest.erl | 70 +++++++++++++++++++++++++++++++ src/rank/rankWork.erl | 93 ++++++++++++++++++++--------------------- src/ranks.erl | 22 ++++++---- src/ranks_app.erl | 2 +- 7 files changed, 154 insertions(+), 58 deletions(-) create mode 100644 include/etsmatch.hrl.in create mode 100644 src/rank/rankTest.erl diff --git a/include/etsmatch.hrl.in b/include/etsmatch.hrl.in new file mode 100644 index 0000000..e6fcd25 --- /dev/null +++ b/include/etsmatch.hrl.in @@ -0,0 +1,21 @@ +-type matchExpression() :: [ matchFunction(), ... ]. + +-type matchFunction() :: { matchHead(), matchConditions(), matchBody()}. + +-type matchHead() :: matchVariable() | '_' | { matchHeadPart(), ...}. + +-type matchHeadPart() :: term() | matchVariable() | '_'. + +-type matchVariable() :: '$'. + +-type matchConditions() :: [ matchCondition(), ...] | []. + +-type matchCondition() :: { guardFunction() } | { guardFunction(), conditionExpression(), ... }. +-type boolFunction() :: is_atom | is_float | is_integer | is_list | is_number | is_pid | is_port | is_reference | is_tuple | is_map | map_is_key | is_binary | is_function | is_record | 'and' | 'or' | 'not' | 'xor' | 'andalso' | 'orelse'. +-type conditionExpression() :: exprMatchVariable() | { guardFunction() } | { guardFunction(), conditionExpression(), ... } | termConstruct(). +-type exprMatchVariable() :: matchVariable() (bound in the MatchHead) | '$_' | '$$' +-type termConstruct():: {{}} | {{ conditionExpression(), ... }} | [] | [conditionExpression(), ...] | #{} | #{term() => conditionExpression(), ...} | nonCompositeTerm() | constant(). +-type nonCompositeTerm() :: term(). %% (not list or tuple or map) +-type constant() :: {const, term()}. +-type guardFunction() :: boolFunction() | abs | element | hd | length | map_get | map_size | node | round | size | bit_size | tl | trunc | '+' | '-' | '*' | 'div' | 'rem' | 'band' | 'bor' | 'bxor' | 'bnot' | 'bsl' | 'bsr' | '>' | '>=' | '<' | '=<' | '=:=' | '==' | '=/=' | '/=' | self. +-type matchBody() :: [ conditionExpression(), ... ]. diff --git a/include/ranks.hrl b/include/ranks.hrl index f5de77a..80ed542 100644 --- a/include/ranks.hrl +++ b/include/ranks.hrl @@ -25,6 +25,8 @@ , rankTypeNScore :: term() }). +-define(AllRankType, [rankType1Score, rankType2Score, rankTypeNScore]). + -record(rankInfo, { rank :: integer() , key :: integer() diff --git a/src/rank/rankMgr.erl b/src/rank/rankMgr.erl index 7f8b1ae..a3ef246 100644 --- a/src/rank/rankMgr.erl +++ b/src/rank/rankMgr.erl @@ -35,7 +35,7 @@ handleCall({mInitRank, RankType, CntLimit, CntMax}, State, _FROM) -> _ -> ets:new(RankType, [ordered_set, public, named_table, {write_concurrency, auto}, {read_concurrency, true}, {decentralized_counters, false}]), NewState = State#{RankType => {CntLimit, CntMax}}, - gtKvsToBeam:load(?ranksLimit, maps:to_list(NewState)), + rsKvsToBeam:load(?ranksLimit, maps:to_list(NewState)), {reply, ok, NewState} end; handleCall(_Msg, _State, _FROM) -> diff --git a/src/rank/rankTest.erl b/src/rank/rankTest.erl new file mode 100644 index 0000000..359bd42 --- /dev/null +++ b/src/rank/rankTest.erl @@ -0,0 +1,70 @@ +-module(rankTest). + +-behavior(gen_srv). + +-include("ranks.hrl"). + +-export([ + start_link/2 + , start/3 +]). + +-export([ + init/1 + , handleCall/3 + , handleCast/2 + , handleInfo/2 + , terminate/2 + , code_change/3 +]). + +start(Cnt, Num, Limit) -> + ranks:startWork(4) , + [ranks:initRank(RankType, ceil(Limit * 0.6), Limit) || RankType <- ?AllRankType], + doTest(Cnt, Num). + +doTest(0, _) -> + ok; +doTest(Cnt, Num) -> + start_link(Cnt, Num), + doTest(Cnt - 1, Num). + +-record(state, {id, num}). + +%% ******************************************** API ******************************************************************* +start_link(Id, Num) -> + gen_srv:start_link(?MODULE, {Id, Num}, []). + +%% ******************************************** callback ************************************************************** +init({Id, Num}) -> + ranks:updateInfo(Id, {?publicInfoPos, {Id, Num, self()}}), + {ok, #state{id = Id, num = Num}, 0}. + +handleCall(_Msg, _State, _FROM) -> + {reply, ok}. + +handleCast(_Msg, _State) -> + kpS. + +handleInfo(timeout, #state{id = Id, num = Num} = State) -> + RankType = lists:nth(rand:uniform(3), ?AllRankType), + Score = rand:uniform(10000), + ranks:updateScore(RankType, Id, {Score, Id}), + NewNum = Num - 1, + case NewNum < 0 of + true -> + io:format("test over ~p~n", [Id]), + {stop, normal, State}; + _ -> + {noreply, State#state{num = NewNum}, 0} + end; +handleInfo(_Msg, _State) -> + kpS. + +terminate(_Reason, _State) -> + ok. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. +%% ****************************************************** logic ******************************************************** + diff --git a/src/rank/rankWork.erl b/src/rank/rankWork.erl index 13b445c..a17bf43 100644 --- a/src/rank/rankWork.erl +++ b/src/rank/rankWork.erl @@ -46,12 +46,12 @@ code_change(_OldVsn, State, _Extra) -> {ok, State}. %% ****************************************************** logic ******************************************************** -mUpdateScore(Key, RankType, Score) -> +mUpdateScore(RankType, Key, Score) -> {_RankLimit, RankMax} = ?ranksLimit:getV(RankType), RankPos = ?ranksCfg:getV(RankType), - case ets:lookup(etsRankInfo, Key) of - [RankRecord] -> - OldScore = element(RankPos, RankRecord), + try ets:lookup_element(?etsRankInfo, Key, RankPos) of + OldScore -> + ets:delete(RankType, OldScore), RankSize = ets:info(RankType, size), @@ -60,42 +60,43 @@ mUpdateScore(Key, RankType, Score) -> FirstKey = ets:first(RankType), case Score > FirstKey of true -> - ets:insert(RankType, Score), + ets:insert(RankType, {Score, Key}), ets:delete(RankType, FirstKey); _ -> ignore end; _ -> - ets:insert(RankType, Score) + ets:insert(RankType, {Score, Key}) end, - ets:update_element(etsRankInfo, Key, {RankPos, Score}); - _ -> - %% 插入新的数据 - RankSize = ets:info(RankType, size), - case RankSize >= RankMax of - true -> - FirstKey = ets:first(RankType), - case Score > FirstKey of - true -> - ets:insert(RankType, Score), - ets:delete(RankType, FirstKey), - NewRecord = #etsRankRecord{key = Key}, - RankRecord = setelement(RankPos, NewRecord, Score), - ets:insert(etsRankInfo, RankRecord); - _ -> - ignore - end; - _ -> - ets:insert(RankType, Score), - NewRecord = #etsRankRecord{key = Key}, - RankRecord = setelement(RankPos, NewRecord, Score), - ets:insert(etsRankInfo, RankRecord) - end + ets:update_element(?etsRankInfo, Key, {RankPos, Score}) + catch _:_ -> + + %% 插入新的数据 + RankSize = ets:info(RankType, size), + case RankSize >= RankMax of + true -> + FirstKey = ets:first(RankType), + case Score > FirstKey of + true -> + ets:insert(RankType, {Score, Key}), + ets:delete(RankType, FirstKey), + NewRecord = #etsRankRecord{key = Key}, + RankRecord = setelement(RankPos, NewRecord, Score), + ets:insert(?etsRankInfo, RankRecord); + _ -> + ignore + end; + _ -> + ets:insert(RankType, {Score, Key}), + NewRecord = #etsRankRecord{key = Key}, + RankRecord = setelement(RankPos, NewRecord, Score), + ets:insert(?etsRankInfo, RankRecord) + end end, {mayReply, ok}. mUpdateInfo(Key, RecordKvs) -> - ets:update_element(etsRankInfo, Key, RecordKvs), + ets:update_element(?etsRankInfo, Key, RecordKvs), {mayReply, ok}. mGetRankInfo(RankType, MyKey, Cnt, Page, PageInfo) -> @@ -105,14 +106,13 @@ mGetRankInfo(RankType, MyKey, Cnt, Page, PageInfo) -> SelfRank = case Page of 0 -> - case ets:lookup_element(etsRankInfo, MyKey) of - [RankRecord] -> - RankPos = ?ranksCfg:getV(RankType), - CurScore = element(RankPos, RankRecord), - MyIndex = ets:select_count(RankType, [{{'$1', '$2'}, [{'>=', '$1', CurScore}], [true]}]), - max(RankLimit, MyIndex); - _ -> - -1 + RankPos = ?ranksCfg:getV(RankType), + try ets:lookup_element(?etsRankInfo, MyKey, RankPos) of + CurScore -> + MyIndex = ets:select_count(RankType, [{{'$1', '$2'}, [{'>=', '$1', {const, CurScore}}], [true]}]), + ?IIF(MyIndex > RankLimit, -1, MyIndex) + catch _:_ -> + -1 end; _ -> 0 @@ -129,9 +129,8 @@ mGetRankInfo(RankType, MyKey, Cnt, Page, PageInfo) -> KeyTerm when KeyTerm == <<"">>; KeyTerm == "" -> [{'$1', [], ['$1']}]; KeyTerm -> - % ets:fun2ms(fun({K, _V} = T) when K < KeyTerm -> T end) - [{{'$1', '$2'}, [{'<', '$1', KeyTerm}], ['$_']}] - catch _C:_R -> + [{{'$1', '$2'}, [{'<', '$1', {const, KeyTerm}}], ['$_']}] + catch _:_ -> [{'$1', [], ['$1']}] end end, @@ -167,10 +166,10 @@ makeRankData([], _Idx, LastKey, Acc) -> _ -> {erlang:term_to_binary(LastKey), lists:reverse(Acc)} end; -makeRankData([{CurKey, Score} | KeyIds], Idx, LastKey, Acc) -> - case ets:lookup(ets_pub_rank_info, CurKey) of - [OneData] -> - makeRankData(KeyIds, Idx + 1, CurKey, [#rankInfo{rank = Idx, key = CurKey, publicInfo = element(?publicInfoPos, OneData), rankTypeScore = Score} | Acc]); - _ -> - makeRankData(KeyIds, Idx, LastKey, Acc) +makeRankData([{Score, CurKey} | KeyIds], Idx, LastKey, Acc) -> + try ets:lookup_element(?etsRankInfo, CurKey, ?publicInfoPos) of + PublicInfo -> + makeRankData(KeyIds, Idx + 1, CurKey, [#rankInfo{rank = Idx, key = CurKey, publicInfo = PublicInfo, rankTypeScore = Score} | Acc]) + catch _:_ -> + makeRankData(KeyIds, Idx, LastKey, Acc) end. \ No newline at end of file diff --git a/src/ranks.erl b/src/ranks.erl index 6196807..c88d801 100644 --- a/src/ranks.erl +++ b/src/ranks.erl @@ -9,7 +9,7 @@ , initRank/3 %% 创建新的类型排行榜 , updateScore/3 %% 更新某类型的排行榜分数 , updateInfo/2 %% 更新公共信息 - , getRankInfo/4 %% 获取排行某页的信息 + , getRankInfo/5 %% 获取排行某页的信息 ]). start() -> @@ -18,6 +18,7 @@ start() -> stop() -> application:stop(ranks). +-spec workName(Idx :: integer()) -> atom(). workName(Idx) -> binary_to_atom(<<"$rankWork_", (integer_to_binary(Idx))/binary>>). @@ -34,35 +35,38 @@ startWork(Cnt) when Cnt > 0 -> [supervisor:start_child(rankWork_sup, [WorkName]) || {_Idx, WorkName} <- NameList], CfgList = [{?workCnt, Cnt} | NameList], Fields = record_info(fields, etsRankRecord), - gtKvsToBeam:load(?ranksCfg, fieldIdx(Fields, 1, CfgList)), + rsKvsToBeam:load(?ranksCfg, fieldIdx(Fields, 2, CfgList)), ok; _Cnt -> {error, started} end. +-spec initRank(RankType :: atom(), CntLimit :: non_neg_integer(), CntMax :: non_neg_integer()) -> ok | {error, atom()}. initRank(RankType, CntLimit, CntMax) -> gen_srv:call(rankMgr, {mInitRank, RankType, CntLimit, CntMax}). -%% 根据排行榜类型更新分数 需要根据key 分配到指定的排行榜工程进程执行 +%% 根据排行榜类型更新分数 需要根据key 分配到指定的排行榜工程进程执行 Score 是个元组 {Score1, Score2, ..., Key} +-spec updateScore(RankType :: atom(), Key :: term(), Score :: tuple()) -> no_return(). updateScore(RankType, Key, Score) -> WorkName = ?ranksCfg:getV(erlang:phash2(Key, ?ranksCfg:getV(?workCnt))), - RankPos = ?ranksCfg:getV(RankType), %% 这里就看业务层面是否需要同步更新分数了 %% 同步请求 %% gen_srv:clfn(WorkName, rank_work, mUpdateScore, [Key, RankPos, Score]), %% 异步请求 - gen_srv:csfn(WorkName, rank_work, mUpdateScore, [Key, RankPos, Score]). + gen_srv:csfn(WorkName, rankWork, mUpdateScore, [RankType, Key, Score]). %% 更新非排行榜分数的其他其他字段的信息 需要根据key 分配到指定的排行榜工程进程执行 +-spec updateInfo(Key :: term(), RecordKvs :: {non_neg_integer(), term()} | [{non_neg_integer(), term()}, ...]) -> no_return(). updateInfo(Key, RecordKvs) -> WorkName = ?ranksCfg:getV(erlang:phash2(Key, ?ranksCfg:getV(?workCnt))), %% 这里就看业务层面是否需要同步更新信息了 %% 同步请求 %% gen_srv:clfn(WorkName, rank_work, mUpdateInfo, [Key, RecordKvs]), %% 异步请求 - gen_srv:csfn(WorkName, rank_work, mUpdateInfo, [Key, RecordKvs]). + gen_srv:csfn(WorkName, rankWork, mUpdateInfo, [Key, RecordKvs]). -%% 根据排行榜类型 获取指定数量 指定页的排行榜的信息 不需要在排行榜工作进程执行 -getRankInfo(RankType, Cnt, Page, PageInfo) -> - rankWork:mGetRankInfo(RankType, Cnt, Page, PageInfo). +%% 根据排行榜类型 获取指定数量 指定页的排行榜的信息 不需要在排行榜工作进程执行 返回{IsOver 是否获取到所有数据, SelfRank -1 未上榜 0 未查询自己排名 名次, RankData 排行榜数据}. +-spec getRankInfo(RankType :: atom(), MyKey :: term(), Cnt :: non_neg_integer(), Page :: integer(), PageInfo :: binary()) -> {IsOver :: boolean(), SelfRank :: integer(), RankData :: list()}. +getRankInfo(RankType, MyKey, Cnt, Page, PageInfo) -> + rankWork:mGetRankInfo(RankType, MyKey, Cnt, Page, PageInfo). diff --git a/src/ranks_app.erl b/src/ranks_app.erl index 1167e6a..729e9be 100644 --- a/src/ranks_app.erl +++ b/src/ranks_app.erl @@ -7,7 +7,7 @@ -export([start/2, stop/1]). start(_StartType, _StartArgs) -> - gtKvsToBeam:load(?ranksCfg, [{?workCnt, 0}]), + rsKvsToBeam:load(?ranksCfg, [{?workCnt, 0}]), ranks_sup:start_link(). stop(_State) ->