-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Telemetry/use configuration based strategy for batch telemetry (#9)
* Create skeleton module for Pacer.Config * Setup first base case unit test for Pacer.Config.batch_telemetry_options/1 * Add unit test case showing batch_telemetry_options passed through at the module level * Add unit case demonstrating behavior of generated Module.__config__/1 behavior when no config options are passed * Allow batch_telemetry_options to be passed at the workflow level * Add implementation for Config.batch_telemetry_options/1 * Add test case for when only global batch_telemetry_options are provided * Add test case for module-level batch_telemetry_config * Add unit test demonstrating behavior of module-level config overriding global config; clean up cached terms in persistent term between test runs * Bump version one minor version up -- Backwards incompatible change coming in how telemetry is metadata is handled in batched resolvers * Fix typo in moduledoc for Pacer.Config * Change assertions on config_test functions to expect keyword lists instead of maps * Add test case for mfa support on batch_telemetry_options global config * Add unit test case capturing support for mfa style batch_telemetry_options on workflow modules directly * Add unit test demonstrating expected behavior when batch_telemetry_options are set with mfa style args in both global and workflow-level config * Rework batch_telemetry_options configuration to allow for mfa-style args * Add test cases for fetch_batch_telemetry_options/1 * Add docs to batch telemetry option-related configuration functions * Add unit test asserting that batched resolvers inject user-provided telemetery config into event metadata when config is a keyword list * Clean up persistent term on all tests and add unit test case for batch telemetry metadata injection from MFA style config * Inject user-provided telemetry metadata into [:pacer, :execute_vertex, ...] events fired inside of batched resolvers * Add docs on Telemetry events and detail how users can provide their own metadata to inject into batched resolver telemetry events * Set version to 0.1.2 * Move the setup/cleanup to run for entire test suite on workflow config
- Loading branch information
Showing
5 changed files
with
377 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
defmodule Pacer.Config do | ||
@moduledoc """ | ||
The #{inspect(__MODULE__)} module provides functions for | ||
extracting user-provided configuration values specific | ||
to Pacer. | ||
""" | ||
|
||
@doc """ | ||
Fetches configuration options for extending the metadata provided in the | ||
`[:pacer, :execute_vertex, :start | :stop | :exception]` events for batched resolvers. | ||
The configuration must be set under the key `:batch_telemetry_options` at the application | ||
level (i.e., `Application.get_env(:pacer, :batch_telemetry_options)`) or when defining the | ||
workflow itself (`use Pacer.Workflow, batch_telemetry_options: <opts>`). | ||
The batch_telemetry_options defined by the user must be either: | ||
- a keyword list, or | ||
- a {module, function, args} mfa tuple; when invoked, this function must return a keyword list | ||
The keyword list of values returned by the mfa-style config, or the hardcoded keyword list, is | ||
fetched and converted into a map that gets merged into the telemetry event metadata for batched resolvers. | ||
""" | ||
@spec batch_telemetry_options(module()) :: keyword() | {module(), atom(), list()} | ||
def batch_telemetry_options(workflow_module) do | ||
case :persistent_term.get({__MODULE__, workflow_module, :batch_telemetry_options}, :unset) do | ||
:unset -> fetch_and_write({workflow_module, :batch_telemetry_options}) | ||
config -> config | ||
end | ||
end | ||
|
||
@doc """ | ||
Takes the batch_telemetry_options configuration, invoking mfa-style config if available, | ||
and converts the batch_telemetry_options keyword list into a map that gets merged into | ||
the metadata for the `[:pacer, :execute_vertex, :start | :stop | :exception]` events for | ||
batched resolvers. | ||
""" | ||
@spec fetch_batch_telemetry_options(module()) :: map() | ||
def fetch_batch_telemetry_options(workflow_module) do | ||
case batch_telemetry_options(workflow_module) do | ||
{mod, fun, args} -> Map.new(apply(mod, fun, args)) | ||
opts -> Map.new(opts) | ||
end | ||
end | ||
|
||
@spec fetch_and_write({workflow_module, :batch_telemetry_options}) :: | ||
keyword() | {module(), atom(), list()} | ||
when workflow_module: module() | ||
defp fetch_and_write({workflow_module, :batch_telemetry_options = key}) do | ||
global_config = Application.get_env(:pacer, key) || [] | ||
module_config = workflow_module.__config__(key) || [] | ||
|
||
cond do | ||
is_list(global_config) && is_list(module_config) -> | ||
global_config | ||
|> Keyword.merge(module_config) | ||
|> tap(&:persistent_term.put({__MODULE__, workflow_module, key}, &1)) | ||
|
||
match?({_m, _f, _a}, module_config) || is_list(module_config) -> | ||
tap(module_config, &:persistent_term.put({__MODULE__, workflow_module, key}, &1)) | ||
|
||
match?({_m, _f, _a}, global_config) || is_list(global_config) -> | ||
tap(global_config, &:persistent_term.put({__MODULE__, workflow_module, key}, &1)) | ||
|
||
true -> | ||
tap([], &:persistent_term.put({__MODULE__, workflow_module, key}, &1)) | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
defmodule Pacer.ConfigTest do | ||
use ExUnit.Case, async: false | ||
|
||
alias Pacer.ConfigTest.NoOptions | ||
alias Pacer.Config | ||
|
||
setup do | ||
default = Application.get_env(:pacer, :batch_telemetry_options) | ||
|
||
on_exit(fn -> | ||
:persistent_term.erase({Config, NoOptions, :batch_telemetry_options}) | ||
:persistent_term.erase({Config, TestBatchConfig, :batch_telemetry_options}) | ||
Application.put_env(:pacer, :batch_telemetry_options, default) | ||
end) | ||
|
||
:ok | ||
end | ||
|
||
describe "batch_telemetry_options/1" do | ||
defmodule NoOptions do | ||
use Pacer.Workflow | ||
|
||
graph do | ||
field(:foo) | ||
end | ||
end | ||
|
||
test "returns an empty map if no user-provided options are available" do | ||
assert Config.batch_telemetry_options(NoOptions) == [] | ||
end | ||
|
||
test "returns global batch_telemetry_options if no module-level options are provided" do | ||
Application.put_env(:pacer, :batch_telemetry_options, foo: "bar") | ||
assert Config.batch_telemetry_options(NoOptions) == [foo: "bar"] | ||
end | ||
|
||
test "accepts {module, function, args} tuples for batch_telemetry_options from global config" do | ||
Application.put_env(:pacer, :batch_telemetry_options, {MyTelemetryOptions, :run, []}) | ||
assert Config.batch_telemetry_options(NoOptions) == {MyTelemetryOptions, :run, []} | ||
end | ||
|
||
defmodule TestBatchConfig do | ||
use Pacer.Workflow, batch_telemetry_options: [batched: "config"] | ||
|
||
graph do | ||
field(:foo) | ||
end | ||
end | ||
|
||
test "returns module-level options when provided" do | ||
assert Config.batch_telemetry_options(TestBatchConfig) == [batched: "config"] | ||
end | ||
|
||
test "module-level batch_telemetry_options overrides global batch_telemetry_options" do | ||
Application.put_env(:pacer, :batch_telemetry_options, | ||
batched: "this value should be overridden" | ||
) | ||
|
||
assert Config.batch_telemetry_options(TestBatchConfig) == [batched: "config"] | ||
end | ||
|
||
defmodule TestConfigWithMFA do | ||
use Pacer.Workflow, batch_telemetry_options: {__MODULE__, :batch_telemetry_opts, []} | ||
|
||
graph do | ||
field(:foo) | ||
end | ||
end | ||
|
||
test "returns {module, function, args} options stored in module config" do | ||
assert Config.batch_telemetry_options(TestConfigWithMFA) == | ||
{TestConfigWithMFA, :batch_telemetry_opts, []} | ||
end | ||
|
||
test "module config overrides global config when both are present and use {module, function, args} style config" do | ||
Application.put_env(:pacer, :batch_telemetry_options, {PacerGlobal, :default_options, []}) | ||
|
||
assert Config.batch_telemetry_options(TestConfigWithMFA) == | ||
{TestConfigWithMFA, :batch_telemetry_opts, []} | ||
end | ||
end | ||
|
||
describe "fetch_batch_telemetry_options/1" do | ||
defmodule MyWorkflowExample do | ||
use Pacer.Workflow | ||
|
||
graph do | ||
field(:foo) | ||
end | ||
|
||
def default_options do | ||
[ | ||
foo: "bar", | ||
baz: "quux" | ||
] | ||
end | ||
end | ||
|
||
test "invokes {module, fun, args} style config when present and converts the keyword list returned into a map" do | ||
Application.put_env( | ||
:pacer, | ||
:batch_telemetry_options, | ||
{MyWorkflowExample, :default_options, []} | ||
) | ||
|
||
assert Config.fetch_batch_telemetry_options(MyWorkflowExample) == %{foo: "bar", baz: "quux"} | ||
end | ||
|
||
test "converts keyword list style configuration into a map" do | ||
Application.put_env(:pacer, :batch_telemetry_options, foo: "bar", baz: "quux") | ||
|
||
assert Config.fetch_batch_telemetry_options(MyWorkflowExample) == %{foo: "bar", baz: "quux"} | ||
end | ||
end | ||
end |
Oops, something went wrong.