feat(rocksdb): WIP

This commit is contained in:
ieQu1 2022-12-22 14:56:58 +01:00 committed by Andrew Mayorov
parent 52964e2bfa
commit 3248f396e0
No known key found for this signature in database
GPG Key ID: 2837C62ACFBFED5D
1 changed files with 98 additions and 16 deletions

View File

@ -1,5 +1,5 @@
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% Copyright (c) 2020-2022 EMQ Technologies Co., Ltd. All Rights Reserved. %% Copyright (c) 2022 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -16,11 +16,21 @@
-module(emqx_replay_message_storage). -module(emqx_replay_message_storage).
-export([open/2]). %% API:
-export([open/2, close/1]).
-export([store/5]). -export([store/5]).
-export([make_iterator/3]). -export([make_iterator/3]).
%% Debug/troubleshooting:
-export([make_message_key/3, compute_topic_hash/1, hash/2, combine/3]).
-export_type([db/0, iterator/0]).
%%================================================================================
%% Type declarations
%%================================================================================
%% see rocksdb:db_options() %% see rocksdb:db_options()
-type options() :: proplists:proplist(). -type options() :: proplists:proplist().
@ -34,29 +44,64 @@
db :: rocksdb:db_handle() db :: rocksdb:db_handle()
}). }).
-record(it, {
handle :: rocksdb:itr_handle(),
topic_filter :: emqx_topic:words(),
bitmask :: integer(),
start_time :: time()
}).
-opaque db() :: #db{}. -opaque db() :: #db{}.
-opaque iterator() :: _TODO.
-opaque iterator() :: #it{}.
%%================================================================================
%% API funcions
%%================================================================================
-spec open(file:filename_all(), options()) -> -spec open(file:filename_all(), options()) ->
{ok, db()} | {error, _TODO}. {ok, db()} | {error, _TODO}.
open(Filename, Options) -> open(Filename, Options) ->
#db{ case rocksdb:open(Filename, [{create_if_missing, true}, Options]) of
db = rocksdb:open(Filename, Options) {ok, Handle} ->
}. {ok, #db{db = Handle}};
Error ->
Error
end.
-spec close(db()) -> ok | {error, _}.
close(#db{db = DB}) ->
rocksdb:close(DB).
-spec store(db(), emqx_guid:guid(), time(), topic(), binary()) -> -spec store(db(), emqx_guid:guid(), time(), topic(), binary()) ->
ok. ok.
store(DB, MessageID, PublishedAt, Topic, MessagePayload) -> store(#db{db = DB}, MessageID, PublishedAt, Topic, MessagePayload) ->
Key = make_message_key(MessageID, Topic, PublishedAt), Key = make_message_key(MessageID, Topic, PublishedAt),
Value = make_message_value(Topic, MessagePayload), Value = make_message_value(Topic, MessagePayload),
rocksdb:put(DB, Key, Value, [{sync, true}]). rocksdb:put(DB, Key, Value, [{sync, true}]).
-spec make_iterator(db(), emqx_topic:words(), time()) -> -spec make_iterator(db(), emqx_topic:words(), time() | earliest) ->
{ok, iterator()} | {error, invalid_start_time}. {ok, iterator()} | {error, invalid_start_time}.
make_iterator(DB, TopicFilter, StartTime) -> make_iterator(#db{db = DBHandle}, TopicFilter, StartTime) ->
case rocksdb:iterator(DBHandle, []) of
{ok, ITHandle} ->
#it{
handle = ITHandle,
topic_filter = TopicFilter,
start_time = StartTime,
bitmask = make_bitmask(TopicFilter)
};
Err ->
Err
end.
-spec next(iterator()) -> {value, binary()} | none.
next(It) ->
error(noimpl). error(noimpl).
%% %%================================================================================
%% Internal exports
%%================================================================================
-define(TOPIC_LEVELS_ENTROPY_BITS, [8, 8, 32, 16]). -define(TOPIC_LEVELS_ENTROPY_BITS, [8, 8, 32, 16]).
@ -72,16 +117,53 @@ combine(TopicHash, PublishedAt, MessageID) ->
compute_topic_hash(Topic) -> compute_topic_hash(Topic) ->
compute_topic_hash(Topic, ?TOPIC_LEVELS_ENTROPY_BITS, 0). compute_topic_hash(Topic, ?TOPIC_LEVELS_ENTROPY_BITS, 0).
compute_topic_hash([], [Bits | BitsRest], Acc) -> hash(Input, Bits) ->
Hash = hash(<<"/">>, Bits), % at most 32 bits
compute_topic_hash([], BitsRest, Acc bsl Bits + Hash); erlang:phash2(Input, 1 bsl Bits).
-spec make_bitmask(emqx_topic:words()) -> integer().
make_bitmask(TopicFilter) ->
make_bitmask(TopicFilter, ?TOPIC_LEVELS_ENTROPY_BITS).
%%================================================================================
%% Internal functions
%%================================================================================
compute_topic_hash(LevelsRest, [Bits], Acc) -> compute_topic_hash(LevelsRest, [Bits], Acc) ->
Hash = hash(LevelsRest, Bits), Hash = hash(LevelsRest, Bits),
Acc bsl Bits + Hash; Acc bsl Bits + Hash;
compute_topic_hash([], [Bits | BitsRest], Acc) ->
Hash = hash(<<"/">>, Bits),
compute_topic_hash([], BitsRest, Acc bsl Bits + Hash);
compute_topic_hash([Level | LevelsRest], [Bits | BitsRest], Acc) -> compute_topic_hash([Level | LevelsRest], [Bits | BitsRest], Acc) ->
Hash = hash(Level, Bits), Hash = hash(Level, Bits),
compute_topic_hash(LevelsRest, BitsRest, Acc bsl Bits + Hash). compute_topic_hash(LevelsRest, BitsRest, Acc bsl Bits + Hash).
hash(Input, Bits) -> make_bitmask(LevelsRest, [Bits], Acc) ->
% at most 32 bits Hash = hash(LevelsRest, Bits),
erlang:phash2(Input, 1 bsl Bits). Acc bsl Bits + Hash;
make_bitmask([], [Bits | BitsRest], Acc) ->
Hash = hash(<<"/">>, Bits),
make_bitmask([], BitsRest, Acc bsl Bits + Hash);
make_bitmask([Level | LevelsRest], [Bits | BitsRest], Acc) ->
Hash = case Level of
'+' ->
0;
Bin when is_binary(Bin) ->
1 bsl Bits - 1;
Hash = hash(Level, Bits),
make_bitmask(LevelsRest, BitsRest, Acc bsl Bits + Hash).
%% |123|345|678|
%% foo bar baz
%% |123|000|678| - |123|fff|678|
%% foo + baz
%% |fff|000|fff|
%% |123|000|678|
%% |123|056|678| & |fff|000|fff| = |123|000|678|.