1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
use anyhow::{Context, Result};
use cadence::StatsdClient;
use merino_settings::Settings;
use merino_settings::SuggestionProviderConfig;
use merino_suggest_providers::make_provider_tree;
use merino_suggest_providers::reconfigure_provider_tree;
use merino_suggest_providers::IdMulti;
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::RwLock as TokioRwLock;
#[derive(Clone)]
pub struct SuggestionProviderRef {
pub provider: Arc<TokioRwLock<IdMulti>>,
metrics_client: StatsdClient,
settings: Settings,
}
impl SuggestionProviderRef {
pub async fn init(settings: Settings, metrics_client: StatsdClient) -> Result<Self> {
let mut idm = IdMulti::default();
let _setup_span = tracing::info_span!("suggestion_provider_setup");
tracing::info!(
r#type = "web.configuring-suggesters",
"Setting up suggestion providers"
);
for (name, config) in settings.suggestion_providers.clone() {
idm.add_provider(
name,
make_provider_tree(settings.clone(), config, metrics_client.clone()).await?,
);
}
Ok(Self {
provider: Arc::new(TokioRwLock::new(idm)),
metrics_client,
settings,
})
}
pub async fn reconfigure(
&self,
suggestion_providers: HashMap<String, SuggestionProviderConfig>,
) -> Result<()> {
let mut id_provider = self.provider.write().await;
let type_erased_config = serde_json::to_value(suggestion_providers)
.context("serialized context for provider reconfiguration")?;
reconfigure_provider_tree(
&mut *id_provider,
self.settings.clone(),
type_erased_config,
self.metrics_client.clone(),
)
.await
.context("reconfiguring providers")?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::make_provider_tree;
use anyhow::Result;
use cadence::{SpyMetricSink, StatsdClient};
use merino_settings::{
providers::{
MemoryCacheConfig, MultiplexerConfig, RedisCacheConfig, SuggestionProviderConfig,
},
Settings,
};
#[tokio::test]
async fn test_providers_single() -> Result<()> {
let settings = Settings::load_for_tests();
let config = SuggestionProviderConfig::Null;
let metrics_client = StatsdClient::from_sink("merino-test", SpyMetricSink::new().1);
let provider_tree = make_provider_tree(settings, config, metrics_client).await?;
assert_eq!(provider_tree.name(), "NullProvider");
Ok(())
}
#[tokio::test]
async fn test_providers_complex() -> Result<()> {
let mut settings = Settings::load_for_tests();
settings.debug = true;
let config = SuggestionProviderConfig::Multiplexer(MultiplexerConfig {
providers: vec![
SuggestionProviderConfig::Null,
SuggestionProviderConfig::RedisCache(RedisCacheConfig {
inner: Box::new(SuggestionProviderConfig::MemoryCache(MemoryCacheConfig {
inner: Box::new(SuggestionProviderConfig::WikiFruit),
..Default::default()
})),
..Default::default()
}),
SuggestionProviderConfig::Null,
],
});
let metrics_client = StatsdClient::from_sink("merino-test", SpyMetricSink::new().1);
let provider_tree = make_provider_tree(settings, config, metrics_client).await?;
assert_eq!(
provider_tree.name(),
"Multi(NullProvider, RedisCache(MemoryCache(WikiFruit)), NullProvider)"
);
Ok(())
}
}