From 6a1340636369100a29188f9a63a0cb49a5b0b3dc Mon Sep 17 00:00:00 2001 From: Andrew Mayorov Date: Fri, 4 Aug 2023 16:18:07 +0400 Subject: [PATCH] fix(topicidx): use custom topic words to keep required ordering Otherwise, topic with empty tokens (e.g. `a/b///c`) would have some of their tokens ordered before `#` / `+`, because empty token was represented as empty atom (`''`). --- apps/emqx/src/emqx_topic_index.erl | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/apps/emqx/src/emqx_topic_index.erl b/apps/emqx/src/emqx_topic_index.erl index ac4901a09..48c685f04 100644 --- a/apps/emqx/src/emqx_topic_index.erl +++ b/apps/emqx/src/emqx_topic_index.erl @@ -39,17 +39,18 @@ -export([get_topic/1]). -export([get_record/2]). --type key(ID) :: {[binary() | '+' | '#'], {ID}}. +-type word() :: binary() | '+' | '#'. +-type key(ID) :: {[word()], {ID}}. -type match(ID) :: key(ID). new() -> - ets:new(?MODULE, [public, ordered_set, {write_concurrency, true}]). + ets:new(?MODULE, [public, ordered_set, {read_concurrency, true}]). insert(Filter, ID, Record, Tab) -> - ets:insert(Tab, {{emqx_topic:words(Filter), {ID}}, Record}). + ets:insert(Tab, {{words(Filter), {ID}}, Record}). delete(Filter, ID, Tab) -> - ets:delete(Tab, {emqx_topic:words(Filter), {ID}}). + ets:delete(Tab, {words(Filter), {ID}}). -spec match(emqx_types:topic(), ets:table()) -> match(_ID) | false. match(Topic, Tab) -> @@ -148,7 +149,7 @@ match_filter([H1 | _], [H2 | _]) when H2 > H1 -> stop. match_init(Topic) -> - case emqx_topic:words(Topic) of + case words(Topic) of [W = <<"$", _/bytes>> | Rest] -> % NOTE % This will effectively skip attempts to match special topics to `#` or `+/...`. @@ -168,3 +169,14 @@ get_topic({Filter, _ID}) -> -spec get_record(match(_ID), ets:table()) -> _Record. get_record(K, Tab) -> ets:lookup_element(Tab, K, 2). + +%% + +-spec words(emqx_types:topic()) -> [word()]. +words(Topic) when is_binary(Topic) -> + [word(W) || W <- emqx_topic:tokens(Topic)]. + +-spec word(binary()) -> word(). +word(<<"+">>) -> '+'; +word(<<"#">>) -> '#'; +word(Bin) -> Bin.