prima_datadog/macros/
compare.rs

1/// Run an experiment comparing the execution time of two blocks of code
2/// Example:
3/// ```
4/// # use rand::prelude::*;
5/// # use prima_datadog::compare;
6/// # use std::thread::sleep;
7/// # use std::time::Duration;
8/// # let mut rng = thread_rng();
9/// // This could also be a network request to an experiment management service, or anything you want
10/// let path = rng.gen_range(0..2);
11/// compare!("test", path, || {
12///     sleep(Duration::from_millis(rng.gen_range(5..10)));
13/// }, || {
14///     sleep(Duration::from_millis(rng.gen_range(5..12)));
15/// }; "some" => "tag");
16/// ```
17/// The above code will run the first block passed, and after execution,
18/// will emit a timing metric to datadog. The metric will be named with
19/// the value of [EXPERIMENTS_METRIC_NAME](crate::timing_guard::EXPERIMENTS_METRIC_NAME), and will be tagged with the name
20/// of the experiment ("experiment_name:test"), the path taken ("path_taken:0"),
21/// and any additional tags provided ("some:tag").
22///
23/// The blocks can be arbitrary code, and the macro is async-safe. In an async context,
24/// the timing will continue across await points, which means that if, for example, you
25/// are awaiting a network request, the timing will include the time spent waiting for
26/// the network request to complete.
27///
28/// NOTE: Try to minimise variation in tag values (avoid things like timestamps or ids). See note in lib docs!
29#[macro_export]
30macro_rules! compare {
31    ($name:expr, $path_taken:expr, || $block_1:expr, || $block_2:expr) => {
32    {
33        use $crate::timing_guard::EXPERIMENTS_METRIC_NAME;
34        let prima_datadog_experiment_tags = &[::std::format!("experiment_name:{}", $name), ::std::format!("path_taken:{}", $path_taken)];
35        let _prima_datadog_timing_guard = $crate::Datadog::enter_timing(EXPERIMENTS_METRIC_NAME, prima_datadog_experiment_tags);
36        if $path_taken == 0 {
37            $block_1
38        } else {
39            $block_2
40        }
41    }
42    };
43    ($name:expr, $path_taken:expr, move || $block_1:expr, move || $block_2:expr) => {
44    {
45        use $crate::timing_guard::EXPERIMENTS_METRIC_NAME;
46        let prima_datadog_experiment_tags = &[::std::format!("experiment_name:{}", $name), ::std::format!("path_taken:{}", $path_taken)];
47        let _prima_datadog_timing_guard = $crate::Datadog::enter_timing(EXPERIMENTS_METRIC_NAME, prima_datadog_experiment_tags);
48        if $path_taken == 0 {
49            $block_1
50        } else {
51            $block_2
52        }
53    }
54    };
55    ($name:expr, $path_taken:expr, || $block_1:expr, || $block_2:expr; $( $key:expr => $value:expr ), *) => {
56    {
57        use $crate::timing_guard::EXPERIMENTS_METRIC_NAME;
58        let prima_datadog_experiment_tags = &[::std::format!("experiment_name:{}", $name), ::std::format!("path_taken:{}", $path_taken), $(::std::format!("{}:{}", $key, $value)), *];
59        let _prima_datadog_timing_guard = $crate::Datadog::enter_timing(EXPERIMENTS_METRIC_NAME, prima_datadog_experiment_tags);
60        if $path_taken == 0 {
61            $block_1
62        } else {
63            $block_2
64        }
65    }
66    };
67    ($name:expr, $path_taken:expr, move || $block_1:expr, move || $block_2:expr; $( $key:expr => $value:expr ), *) => {
68    {
69        use $crate::timing_guard::EXPERIMENTS_METRIC_NAME;
70        let prima_datadog_experiment_tags = &[::std::format!("experiment_name:{}", $name), ::std::format!("path_taken:{}", $path_taken), $(::std::format!("{}:{}", $key, $value)), *];
71        let _prima_datadog_timing_guard = $crate::Datadog::enter_timing(EXPERIMENTS_METRIC_NAME, prima_datadog_experiment_tags);
72        if $path_taken == 0 {
73            $block_1
74        } else {
75            $block_2
76        }
77    }
78    };
79}