Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion lib/sanbase/accounts/apikey/apikey.ex
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,13 @@ defmodule Sanbase.Accounts.Apikey do
user tokens for that ID and check if the apikey is generated from any of the
tokens
"""
@apikey_user_cache_ttl 60

@spec apikey_to_user(String.t()) :: {:ok, %User{}} | {:error, String.t()}
def apikey_to_user(apikey) do
with {:ok, {token, _rest}} <- Hmac.split_apikey(apikey),
{_, true} <- {:valid?, Hmac.apikey_valid?(token, apikey)},
{_, {:ok, user}} <- {:user?, User.by_apikey_token(token)} do
{_, {:ok, user}} <- {:user?, fetch_user_by_token_cached(token)} do
{:ok, user}
else
{:valid?, _} ->
Expand All @@ -43,6 +45,16 @@ defmodule Sanbase.Accounts.Apikey do
end
end

defp fetch_user_by_token_cached(token) do
cache_key =
{__MODULE__, :apikey_user, token}
|> Sanbase.Cache.hash()

Sanbase.Cache.get_or_store({cache_key, @apikey_user_cache_ttl}, fn ->
User.by_apikey_token(token)
end)
end

def mask_apikey(apikey) do
apikey_length = String.length(apikey)
# All between the first 6 chars and the last 2 chars will be hidden
Expand Down
12 changes: 10 additions & 2 deletions lib/sanbase/cache/cache.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ defmodule Sanbase.Cache do
@cache_name :sanbase_cache
@max_cache_ttl 86_400

# This is 2^32, so the hashing functions return everything in 0..2^32 range
@phash_upper_bound 4_294_967_296

@compile {:inline, get_or_store_isolated: 4}

@impl Sanbase.Cache.Behaviour
Expand All @@ -21,8 +24,13 @@ defmodule Sanbase.Cache do

@impl Sanbase.Cache.Behaviour
def hash(data) do
:crypto.hash(:sha256, :erlang.term_to_binary(data))
|> Base.encode64()
# Use two phash2 calls for a 64-bit hash space (negligible collision risk)
# instead of SHA256 + term_to_binary + Base64. phash2 operates directly on
# Erlang terms without serialization, making it orders of magnitude faster.
# Slightly change the input to h2 so it returns a different result
h1 = :erlang.phash2(data, @phash_upper_bound)
h2 = :erlang.phash2({:salted, data}, @phash_upper_bound)
<<h1::unsigned-32, h2::unsigned-32>> |> Base.encode16(case: :lower)
end

def name, do: @cache_name
Expand Down
35 changes: 25 additions & 10 deletions lib/sanbase/clickhouse_repo.ex
Original file line number Diff line number Diff line change
Expand Up @@ -54,25 +54,40 @@ defmodule Sanbase.ClickhouseRepo do
end
end

@include_stacktrace_in_ch_queries System.get_env("CLICKHOUSE_INCLUDE_STACKTRACE", "false") ==
"true"

defp add_metadata_to_query(query) do
type = System.get_env("CONTAINER_TYPE") || "all"

request_id = (Process.get(:"$logger_metadata$") || %{}) |> Map.get(:request_id)
{_, [_process_info_call | rest_stacktrace]} = Process.info(self(), :current_stacktrace)

stacktrace =
Enum.take(rest_stacktrace, 5) |> :erlang.term_to_binary() |> :zlib.gzip() |> Base.encode64()

query
|> Sanbase.Clickhouse.Query.add_leading_comment("sanbase_container_type #{type}")
|> Sanbase.Clickhouse.Query.extend_log_comment(%{
log_comment = %{
sanbase_container_type: type,
owner: "backend",
team: "backend",
repo: "sanbase2",
graphql_request_log_id: request_id,
stacktrace: stacktrace
})
graphql_request_log_id: request_id
}

log_comment =
if @include_stacktrace_in_ch_queries do
{_, [_process_info_call | rest_stacktrace]} = Process.info(self(), :current_stacktrace)

stacktrace =
Enum.take(rest_stacktrace, 5)
|> :erlang.term_to_binary()
|> :zlib.gzip()
|> Base.encode64()

Map.put(log_comment, :stacktrace, stacktrace)
else
log_comment
end

query
|> Sanbase.Clickhouse.Query.add_leading_comment("sanbase_container_type #{type}")
|> Sanbase.Clickhouse.Query.extend_log_comment(log_comment)
end

def query_transform(query, args, transform_fn) do
Expand Down
11 changes: 5 additions & 6 deletions lib/sanbase_web/graphql/cache/cachex_provider.ex
Original file line number Diff line number Diff line change
Expand Up @@ -195,14 +195,13 @@ defmodule SanbaseWeb.Graphql.CachexProvider do
defp true_key(key), do: key

defp compress_value(value) do
value
|> :erlang.term_to_binary()
|> :zlib.gzip()
# Use Erlang's built-in compressed binary format instead of gzip.
# This is ~3-5x faster than term_to_binary + zlib.gzip while still
# reducing memory usage. Level 1 gives best speed/size tradeoff.
:erlang.term_to_binary(value, compressed: 1)
end

defp decompress_value(value) do
value
|> :zlib.gunzip()
|> :erlang.binary_to_term()
:erlang.binary_to_term(value)
end
end