Skip to content

Commit

Permalink
chore: add agent and runtime-version metadata
Browse files Browse the repository at this point in the history
Add agent and runtime-version metadata to the first call made by a data
and control client. It uses the process dictionary to manage the state
about whether it has sent the data already.

Add tests that check that the agent data is onlt sent once.
  • Loading branch information
nand4011 committed Jul 18, 2024
1 parent 84e4d18 commit 26f2772
Show file tree
Hide file tree
Showing 6 changed files with 158 additions and 14 deletions.
38 changes: 35 additions & 3 deletions src/lib/momento/internal/scs_control_client.ex
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ defmodule Momento.Internal.ScsControlClient do

@spec list_caches(client :: t()) :: Momento.Responses.ListCaches.t()
def list_caches(client) do
metadata = %{Authorization: client.auth_token}
metadata = create_metadata(client)
list_caches_request = %Momento.Protos.ControlClient.ListCachesRequest{}

case Momento.Protos.ControlClient.ScsControl.Stub.list_caches(
Expand All @@ -60,7 +60,7 @@ defmodule Momento.Internal.ScsControlClient do

@spec create_cache(client :: t(), cache_name :: String.t()) :: Momento.Responses.CreateCache.t()
def create_cache(client, cache_name) do
metadata = %{Authorization: client.auth_token}
metadata = create_metadata(client)

create_cache_request = %Momento.Protos.ControlClient.CreateCacheRequest{
cache_name: cache_name
Expand Down Expand Up @@ -88,7 +88,7 @@ defmodule Momento.Internal.ScsControlClient do

@spec delete_cache(client :: t(), cache_name :: String.t()) :: Momento.Responses.DeleteCache.t()
def delete_cache(client, cache_name) do
metadata = %{Authorization: client.auth_token}
metadata = create_metadata(client)

delete_cache_request = %Momento.Protos.ControlClient.DeleteCacheRequest{
cache_name: cache_name
Expand All @@ -108,4 +108,36 @@ defmodule Momento.Internal.ScsControlClient do
end
end
end

@agent_data_key "__#{__MODULE__}_AGENT_DATA_SENT__"

defp should_send_agent_data? do
:erlang.get(@agent_data_key) == :undefined
end

@spec create_metadata(t()) :: %{required(String.t()) => String.t()}
defp create_metadata(client) do
base_metadata = %{
"authorization" => client.auth_token
}

if should_send_agent_data?() do
:erlang.put(@agent_data_key, true)

Map.merge(base_metadata, %{
"agent" => "elixir:cache:" <> get_library_version(),
"runtime-version" => System.version()
})
else
base_metadata
end
end

@spec get_library_version() :: String.t()
defp get_library_version do
case Application.spec(:gomomento, :vsn) do
nil -> "unknown"
version -> to_string(version)
end
end
end
53 changes: 43 additions & 10 deletions src/lib/momento/internal/scs_data_client.ex
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ defmodule Momento.Internal.ScsDataClient do
with :ok <- validate_cache_name(cache_name),
{:ok, ttl_milliseconds} <- get_ttl_milliseconds(ttl_seconds) do
try do
metadata = %{cache: cache_name, Authorization: data_client.auth_token}
metadata = create_metadata(cache_name, data_client)

set_request = %Momento.Protos.CacheClient.SetRequest{
cache_key: key,
Expand All @@ -75,7 +75,7 @@ defmodule Momento.Internal.ScsDataClient do
def get(data_client, cache_name, key) do
with :ok <- validate_cache_name(cache_name) do
try do
metadata = %{cache: cache_name, Authorization: data_client.auth_token}
metadata = create_metadata(cache_name, data_client)

get_request = %Momento.Protos.CacheClient.GetRequest{cache_key: key}

Expand Down Expand Up @@ -104,7 +104,7 @@ defmodule Momento.Internal.ScsDataClient do
def delete(data_client, cache_name, key) do
with :ok <- validate_cache_name(cache_name) do
try do
metadata = %{cache: cache_name, Authorization: data_client.auth_token}
metadata = create_metadata(cache_name, data_client)

delete_request = %Momento.Protos.CacheClient.DeleteRequest{cache_key: key}

Expand Down Expand Up @@ -141,7 +141,7 @@ defmodule Momento.Internal.ScsDataClient do
{:ok, ttl_milliseconds} <- get_ttl_milliseconds(collection_ttl),
{:ok, transformed_elements} <- transform_sorted_set_elements(elements) do
try do
metadata = %{cache: cache_name, Authorization: data_client.auth_token}
metadata = create_metadata(cache_name, data_client)

sorted_set_put_request = %Momento.Protos.CacheClient.SortedSetPutRequest{
set_name: sorted_set_name,
Expand Down Expand Up @@ -211,7 +211,7 @@ defmodule Momento.Internal.ScsDataClient do
with :ok <- validate_cache_name(cache_name),
:ok <- validate_sorted_set_name(sorted_set_name) do
try do
metadata = %{cache: cache_name, Authorization: data_client.auth_token}
metadata = create_metadata(cache_name, data_client)

start_index =
case start_rank do
Expand Down Expand Up @@ -298,7 +298,7 @@ defmodule Momento.Internal.ScsDataClient do
with :ok <- validate_cache_name(cache_name),
:ok <- validate_sorted_set_name(sorted_set_name) do
try do
metadata = %{cache: cache_name, Authorization: data_client.auth_token}
metadata = create_metadata(cache_name, data_client)

request_min_score =
case min_score do
Expand Down Expand Up @@ -405,7 +405,7 @@ defmodule Momento.Internal.ScsDataClient do
with :ok <- validate_cache_name(cache_name),
:ok <- validate_sorted_set_name(sorted_set_name) do
try do
metadata = %{cache: cache_name, Authorization: data_client.auth_token}
metadata = create_metadata(cache_name, data_client)

remove_request = %Momento.Protos.CacheClient.SortedSetRemoveRequest{
set_name: sorted_set_name,
Expand Down Expand Up @@ -449,7 +449,7 @@ defmodule Momento.Internal.ScsDataClient do
with :ok <- validate_cache_name(cache_name),
:ok <- validate_sorted_set_name(sorted_set_name) do
try do
metadata = %{cache: cache_name, Authorization: data_client.auth_token}
metadata = create_metadata(cache_name, data_client)

request_order =
case sort_order do
Expand Down Expand Up @@ -571,7 +571,7 @@ defmodule Momento.Internal.ScsDataClient do
with :ok <- validate_cache_name(cache_name),
:ok <- validate_sorted_set_name(sorted_set_name) do
try do
metadata = %{cache: cache_name, Authorization: data_client.auth_token}
metadata = create_metadata(cache_name, data_client)

get_scores_request = %Momento.Protos.CacheClient.SortedSetGetScoreRequest{
set_name: sorted_set_name,
Expand Down Expand Up @@ -642,7 +642,7 @@ defmodule Momento.Internal.ScsDataClient do
:ok <- validate_sorted_set_name(sorted_set_name),
{:ok, ttl_milliseconds} <- get_ttl_milliseconds(collection_ttl) do
try do
metadata = %{cache: cache_name, Authorization: data_client.auth_token}
metadata = create_metadata(cache_name, data_client)

increment_request = %Momento.Protos.CacheClient.SortedSetIncrementRequest{
set_name: sorted_set_name,
Expand Down Expand Up @@ -687,4 +687,37 @@ defmodule Momento.Internal.ScsDataClient do

defp get_ttl_milliseconds(ttl),
do: {:error, Momento.Error.invalid_argument("Unable to parse TTL from #{ttl}")}

@agent_data_key "__#{__MODULE__}_AGENT_DATA_SENT__"

defp should_send_agent_data? do
:erlang.get(@agent_data_key) == :undefined
end

@spec create_metadata(String.t(), t()) :: %{required(String.t()) => String.t()}
defp create_metadata(cache_name, data_client) do
base_metadata = %{
"cache" => cache_name,
"authorization" => data_client.auth_token
}

if should_send_agent_data?() do
:erlang.put(@agent_data_key, true)

Map.merge(base_metadata, %{
"agent" => "elixir:cache:" <> get_library_version(),
"runtime-version" => System.version()
})
else
base_metadata
end
end

@spec get_library_version() :: String.t()
defp get_library_version do
case Application.spec(:gomomento, :vsn) do
nil -> "unknown"
version -> to_string(version)
end
end
end
3 changes: 2 additions & 1 deletion src/mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ defmodule Momento.MixProject do
{:google_protos, "~> 0.3"},
{:joken, "~> 2.5"},
{:jason, "~> 1.4"},
{:tls_certificate_check, "~> 1.19"}
{:tls_certificate_check, "~> 1.19"},
{:mock, "~> 0.3.8", only: :test}
]
end

Expand Down
2 changes: 2 additions & 0 deletions src/mix.lock
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
"makeup": {:hex, :makeup, "1.1.0", "6b67c8bc2882a6b6a445859952a602afc1a41c2e08379ca057c0f525366fc3ca", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "0a45ed501f4a8897f580eabf99a2e5234ea3e75a4373c8a52824f6e873be57a6"},
"makeup_elixir": {:hex, :makeup_elixir, "0.16.1", "cc9e3ca312f1cfeccc572b37a09980287e243648108384b97ff2b76e505c3555", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "e127a341ad1b209bd80f7bd1620a15693a9908ed780c3b763bccf7d200c767c6"},
"makeup_erlang": {:hex, :makeup_erlang, "0.1.1", "3fcb7f09eb9d98dc4d208f49cc955a34218fc41ff6b84df7c75b3e6e533cc65f", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "174d0809e98a4ef0b3309256cbf97101c6ec01c4ab0b23e926a9e17df2077cbb"},
"meck": {:hex, :meck, "0.9.2", "85ccbab053f1db86c7ca240e9fc718170ee5bda03810a6292b5306bf31bae5f5", [:rebar3], [], "hexpm", "81344f561357dc40a8344afa53767c32669153355b626ea9fcbc8da6b3045826"},
"mock": {:hex, :mock, "0.3.8", "7046a306b71db2488ef54395eeb74df0a7f335a7caca4a3d3875d1fc81c884dd", [:mix], [{:meck, "~> 0.9.2", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm", "7fa82364c97617d79bb7d15571193fc0c4fe5afd0c932cef09426b3ee6fe2022"},
"nimble_parsec": {:hex, :nimble_parsec, "1.3.1", "2c54013ecf170e249e9291ed0a62e5832f70a476c61da16f6aac6dca0189f2af", [:mix], [], "hexpm", "2682e3c0b2eb58d90c6375fc0cc30bc7be06f365bf72608804fb9cffa5e1b167"},
"protobuf": {:hex, :protobuf, "0.12.0", "58c0dfea5f929b96b5aa54ec02b7130688f09d2de5ddc521d696eec2a015b223", [:mix], [{:jason, "~> 1.2", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "75fa6cbf262062073dd51be44dd0ab940500e18386a6c4e87d5819a58964dc45"},
"ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"},
Expand Down
38 changes: 38 additions & 0 deletions src/test/momento/internal/scs_control_client_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
defmodule Momento.Internal.ScsControlClientTest do
use ExUnit.Case, async: false
import Mock

alias Momento.Internal.ScsControlClient
alias Momento.Protos.ControlClient.ScsControl.Stub

test "agent metadata is only sent on the first call" do
fake_channel = :fake_channel

client = %ScsControlClient{
auth_token: "test_token",
channel: fake_channel
}

with_mock Stub, [:passthrough],
create_cache: fn ^fake_channel, _request, options ->
metadata = Keyword.get(options, :metadata, %{})
send(self(), {:grpc_call, metadata})
{:ok, %{}}
end do
ScsControlClient.create_cache(client, "test_cache")
assert_received {:grpc_call, metadata}
assert Map.has_key?(metadata, "agent")
assert Map.has_key?(metadata, "runtime-version")

ScsControlClient.create_cache(client, "test_cache")
assert_received {:grpc_call, metadata}
refute Map.has_key?(metadata, "agent")
refute Map.has_key?(metadata, "runtime-version")

ScsControlClient.create_cache(client, "test_cache")
assert_received {:grpc_call, metadata}
refute Map.has_key?(metadata, "agent")
refute Map.has_key?(metadata, "runtime-version")
end
end
end
38 changes: 38 additions & 0 deletions src/test/momento/internal/scs_data_client_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
defmodule Momento.Internal.ScsDataClientTest do
use ExUnit.Case, async: false
import Mock

alias Momento.Internal.ScsDataClient
alias Momento.Protos.CacheClient.Scs.Stub

test "agent metadata is only sent on the first call" do
fake_channel = :fake_channel

client = %ScsDataClient{
auth_token: "test_token",
channel: fake_channel
}

with_mock Stub, [:passthrough],
set: fn ^fake_channel, _request, options ->
metadata = Keyword.get(options, :metadata, %{})
send(self(), {:grpc_call, metadata})
{:ok, %{}}
end do
ScsDataClient.set(client, "test_cache", "key1", "value1", 60)
assert_received {:grpc_call, metadata}
assert Map.has_key?(metadata, "agent")
assert Map.has_key?(metadata, "runtime-version")

ScsDataClient.set(client, "test_cache", "key2", "value2", 60)
assert_received {:grpc_call, metadata}
refute Map.has_key?(metadata, "agent")
refute Map.has_key?(metadata, "runtime-version")

ScsDataClient.set(client, "test_cache", "key3", "value3", 60)
assert_received {:grpc_call, metadata}
refute Map.has_key?(metadata, "agent")
refute Map.has_key?(metadata, "runtime-version")
end
end
end

0 comments on commit 26f2772

Please sign in to comment.