mirror of
https://github.com/facebook/sapling.git
synced 2024-10-10 00:45:18 +03:00
RFC: mononoke: add support for string tunables
Summary: Just as for ints and bools, let's add support for strings. A few notes: 1) we don't have AtomicString, so I decided to use ArcSwap<String>. However to make code generation simpler (see tunables-derive) I've added a type alias for that 2) Return type is Arc<String> to avoid unnecessary copies.Another option that we might have here is to put the whole Tunables structure inside ArcSwap, and changing `tunables()` to return Arc<MononookeTunables> instead of &'static MononokeTunables. However that's a bigger change to make. Reviewed By: markbt Differential Revision: D21592390 fbshipit-source-id: 6d3cf340b13f7aef9adb2b1b99ed2bf260033285
This commit is contained in:
parent
cc5d3fafc4
commit
fc7b25b0a2
@ -13,6 +13,7 @@ cached_config = { git = "https://github.com/facebookexperimental/rust-shed.git",
|
||||
fbinit = { git = "https://github.com/facebookexperimental/rust-shed.git", branch = "master" }
|
||||
fbthrift = { git = "https://github.com/facebook/fbthrift.git", branch = "master" }
|
||||
anyhow = "1.0"
|
||||
arc-swap = "0.4"
|
||||
futures = { version = "0.3", features = ["async-await", "compat"] }
|
||||
once_cell = "1.2"
|
||||
serde_json = "1.0"
|
||||
|
@ -10,6 +10,7 @@ use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::Result;
|
||||
use arc_swap::ArcSwap;
|
||||
use cached_config::ConfigHandle;
|
||||
use once_cell::sync::OnceCell;
|
||||
use slog::{debug, warn, Logger};
|
||||
@ -25,6 +26,9 @@ pub fn tunables() -> &'static MononokeTunables {
|
||||
TUNABLES.get_or_init(MononokeTunables::default)
|
||||
}
|
||||
|
||||
// This type exists to simplify code generation in tunables-derive
|
||||
pub type TunableString = ArcSwap<String>;
|
||||
|
||||
#[derive(Tunables, Default, Debug)]
|
||||
pub struct MononokeTunables {
|
||||
mutation_advertise_for_infinitepush: AtomicBool,
|
||||
@ -69,6 +73,7 @@ fn update_tunables(new_tunables: Arc<TunablesStruct>) -> Result<()> {
|
||||
let tunables = tunables();
|
||||
tunables.update_bools(&new_tunables.killswitches);
|
||||
tunables.update_ints(&new_tunables.ints);
|
||||
tunables.update_strings(&new_tunables.strings);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -83,6 +88,7 @@ mod test {
|
||||
struct TestTunables {
|
||||
boolean: AtomicBool,
|
||||
num: AtomicI64,
|
||||
string: TunableString,
|
||||
}
|
||||
|
||||
#[derive(Tunables, Default)]
|
||||
@ -96,6 +102,7 @@ mod test {
|
||||
|
||||
empty.update_bools(&bools);
|
||||
empty.update_ints(&ints);
|
||||
empty.update_strings(&HashMap::new());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -130,4 +137,15 @@ mod test {
|
||||
test.update_ints(&d);
|
||||
assert_eq!(test.get_num(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn update_string() {
|
||||
let mut d = HashMap::new();
|
||||
d.insert("string".to_string(), "value".to_string());
|
||||
|
||||
let test = TestTunables::default();
|
||||
assert_eq!(test.get_string().as_str(), "");
|
||||
test.update_strings(&d);
|
||||
assert_eq!(test.get_string().as_str(), "value");
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ const STRUCT_FIELD_MSG: &str = "Only implemented for named fields of a struct";
|
||||
enum TunableType {
|
||||
Bool,
|
||||
I64,
|
||||
String,
|
||||
}
|
||||
|
||||
#[proc_macro_derive(Tunables)]
|
||||
@ -45,10 +46,19 @@ pub fn derive_tunables(input: proc_macro::TokenStream) -> proc_macro::TokenStrea
|
||||
}
|
||||
|
||||
impl TunableType {
|
||||
fn external_type(&self) -> Ident {
|
||||
fn external_type(&self) -> TokenStream {
|
||||
match self {
|
||||
Self::Bool => quote! { bool },
|
||||
Self::I64 => quote! { i64 },
|
||||
Self::String => quote! { Arc<String> },
|
||||
}
|
||||
}
|
||||
|
||||
fn input_type(&self) -> Ident {
|
||||
match self {
|
||||
Self::Bool => quote::format_ident!("{}", "bool"),
|
||||
Self::I64 => quote::format_ident!("{}", "i64"),
|
||||
Self::String => quote::format_ident!("{}", "String"),
|
||||
}
|
||||
}
|
||||
|
||||
@ -56,9 +66,20 @@ impl TunableType {
|
||||
let method = quote::format_ident!("get_{}", name);
|
||||
let external_type = self.external_type();
|
||||
|
||||
quote! {
|
||||
pub fn #method(&self) -> #external_type {
|
||||
return self.#name.load(std::sync::atomic::Ordering::Relaxed)
|
||||
match &self {
|
||||
Self::Bool | Self::I64 => {
|
||||
quote! {
|
||||
pub fn #method(&self) -> #external_type {
|
||||
return self.#name.load(std::sync::atomic::Ordering::Relaxed)
|
||||
}
|
||||
}
|
||||
}
|
||||
Self::String => {
|
||||
quote! {
|
||||
pub fn #method(&self) -> #external_type {
|
||||
self.#name.load_full()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -90,11 +111,17 @@ where
|
||||
));
|
||||
|
||||
methods.extend(generate_updater_method(
|
||||
names_and_types,
|
||||
names_and_types.clone(),
|
||||
TunableType::I64,
|
||||
quote::format_ident!("update_ints"),
|
||||
));
|
||||
|
||||
methods.extend(generate_updater_method(
|
||||
names_and_types,
|
||||
TunableType::String,
|
||||
quote::format_ident!("update_strings"),
|
||||
));
|
||||
|
||||
methods
|
||||
}
|
||||
|
||||
@ -108,21 +135,37 @@ where
|
||||
{
|
||||
let names = names_and_types.filter(|(_, t)| *t == ty).map(|(n, _)| n);
|
||||
|
||||
let type_ident = ty.external_type();
|
||||
let type_ident = ty.input_type();
|
||||
let mut names = names.peekable();
|
||||
let mut body = TokenStream::new();
|
||||
|
||||
if names.peek().is_some() {
|
||||
body.extend(
|
||||
quote! {
|
||||
for (name, val) in tunables.iter() {
|
||||
match name.as_ref() {
|
||||
#(stringify!(#names) => self.#names.store(*val, std::sync::atomic::Ordering::Relaxed), )*
|
||||
_ => {}
|
||||
match ty {
|
||||
TunableType::I64 | TunableType::Bool => {
|
||||
body.extend(
|
||||
quote! {
|
||||
for (name, val) in tunables.iter() {
|
||||
match name.as_ref() {
|
||||
#(stringify!(#names) => self.#names.store(*val, std::sync::atomic::Ordering::Relaxed), )*
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
)
|
||||
TunableType::String => {
|
||||
body.extend(quote! {
|
||||
for (name, val) in tunables {
|
||||
match name.as_ref() {
|
||||
#(stringify!(#names) => {
|
||||
self.#names.swap(Arc::new(val.clone()));
|
||||
}, )*
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
quote! {
|
||||
@ -154,6 +197,11 @@ fn resolve_type(ty: Type) -> TunableType {
|
||||
match &ident.to_string()[..] {
|
||||
"AtomicBool" => return TunableType::Bool,
|
||||
"AtomicI64" => return TunableType::I64,
|
||||
// TunableString is a type alias of ArcSwap<String>.
|
||||
// p.path.get_ident() returns None for ArcSwap<String>
|
||||
// and it makes it harder to parse it.
|
||||
// We use TunableString as a workaround
|
||||
"TunableString" => return TunableType::String,
|
||||
_ => unimplemented!("{}", UNIMPLEMENTED_MSG),
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user