%%%-------------------------------------------------------------------
%%% @doc Usage statistics for barrel_docdb
%%%
%%% Provides usage statistics collected from RocksDB properties.
%%% This is a thin wrapper around existing RocksDB property access
%%% with no runtime state.
%%%
%%% == Example Usage ==
%%% ```
%%% %% Get usage stats for a single database
%%% {ok, Stats} = barrel_docdb_usage:get_db_usage(<<"mydb">>).
%%% #{
%%% database := <<"mydb">>,
%%% document_count := 15420,
%%% storage_bytes := 104857600,
%%% memtable_size := 2097152,
%%% sst_files_size := 100663296,
%%% last_updated := 1704067200000
%%% } = Stats.
%%%
%%% %% Get usage stats for all databases
%%% {ok, AllStats} = barrel_docdb_usage:get_all_usage().
%%% '''
%%%
%%% @end
%%%-------------------------------------------------------------------
-module(barrel_docdb_usage).
%% API
-export([
get_db_usage/1,
get_all_usage/0
]).
%%====================================================================
%% API
%%====================================================================
%% @doc Get usage statistics for a single database.
%% Returns a map with storage metrics from RocksDB.
-spec get_db_usage(binary()) -> {ok, map()} | {error, term()}.
get_db_usage(DbName) when is_binary(DbName) ->
case barrel_docdb:db_pid(DbName) of
{ok, Pid} ->
case barrel_db_server:get_store_ref(Pid) of
{ok, DbRef} ->
collect_stats(DbName, DbRef);
{error, Reason} ->
{error, Reason}
end;
{error, Reason} ->
{error, Reason}
end.
%% @doc Get usage statistics for all databases.
%% Returns a list of stats maps, one for each database.
-spec get_all_usage() -> {ok, [map()]}.
get_all_usage() ->
DbNames = barrel_docdb:list_dbs(),
Stats = lists:filtermap(
fun(DbName) ->
case get_db_usage(DbName) of
{ok, S} -> {true, S};
{error, _} -> false
end
end,
DbNames
),
{ok, Stats}.
%%====================================================================
%% Internal Functions
%%====================================================================
%% @private
%% Collect stats from RocksDB properties.
collect_stats(DbName, DbRef) ->
%% Get estimated document count
DocCount = get_estimated_keys(DbRef),
%% Get storage size
StorageBytes = get_storage_size(DbRef),
%% Get memtable size
MemtableSize = get_memtable_size(DbRef),
%% Get SST files size
SstFilesSize = get_sst_files_size(DbRef),
Stats = #{
database => DbName,
document_count => DocCount,
storage_bytes => StorageBytes,
memtable_size => MemtableSize,
sst_files_size => SstFilesSize,
last_updated => erlang:system_time(millisecond)
},
{ok, Stats}.
%% @private
%% Get estimated number of keys in the database.
get_estimated_keys(DbRef) ->
case barrel_store_rocksdb:get_property(DbRef, <<"rocksdb.estimate-num-keys">>) of
{ok, Value} -> safe_to_integer(Value);
{error, _} -> 0
end.
%% @private
%% Get total storage size (using get_db_size which tries live data first).
get_storage_size(DbRef) ->
case barrel_store_rocksdb:get_db_size(DbRef) of
{ok, Size} -> Size;
{error, _} -> 0
end.
%% @private
%% Get current memtable size.
get_memtable_size(DbRef) ->
case barrel_store_rocksdb:get_property(DbRef, <<"rocksdb.cur-size-all-mem-tables">>) of
{ok, Value} -> safe_to_integer(Value);
{error, _} -> 0
end.
%% @private
%% Get total SST files size.
get_sst_files_size(DbRef) ->
case barrel_store_rocksdb:get_property(DbRef, <<"rocksdb.total-sst-files-size">>) of
{ok, Value} -> safe_to_integer(Value);
{error, _} -> 0
end.
%% @private
%% Safely convert binary to integer.
safe_to_integer(Bin) when is_binary(Bin) ->
barrel_lib:safe(fun() -> binary_to_integer(Bin) end, 0).