Skip to content

Commit a52c50f

Browse files
Floris Bruynoogheloewenheim
andauthored
Hold a mutex while refreshing the token (#41)
There is no point in asking the provider for multiple tokens at the same time. With a high enough request rate this results in a thundering herd of refresh requests and eventually the metadata service rejecting the requests with HTTP 429 status code. Could be improved by having a per-scope lock for `CustomServiceAccount`. Smarter approaches are possible, but this is a good first approach to avoid a thundering herd on the refresh. Co-authored-by: Sebastian Zivota <[email protected]>
1 parent 5e95161 commit a52c50f

File tree

3 files changed

+32
-16
lines changed

3 files changed

+32
-16
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ rustls = "0.20.2"
2424
rustls-pemfile = "0.2.1"
2525
serde = {version = "1.0", features = ["derive", "rc"]}
2626
serde_json = "1.0"
27-
tokio = { version = "1.1", features = ["fs"] }
27+
tokio = { version = "1.1", features = ["fs", "sync"] }
2828
url = "2"
2929
which = "4.2"
3030
async-trait = "0.1"

src/authentication_manager.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use async_trait::async_trait;
2+
use tokio::sync::Mutex;
23

34
use crate::error::Error;
45
use crate::types::{HyperClient, Token};
@@ -16,9 +17,18 @@ pub(crate) trait ServiceAccount: Send + Sync {
1617
pub struct AuthenticationManager {
1718
pub(crate) client: HyperClient,
1819
pub(crate) service_account: Box<dyn ServiceAccount>,
20+
refresh_mutex: Mutex<()>,
1921
}
2022

2123
impl AuthenticationManager {
24+
pub(crate) fn new(client: HyperClient, service_account: Box<dyn ServiceAccount>) -> Self {
25+
Self {
26+
client,
27+
service_account,
28+
refresh_mutex: Mutex::new(()),
29+
}
30+
}
31+
2232
/// Requests Bearer token for the provided scope
2333
///
2434
/// Token can be used in the request authorization header in format "Bearer {token}"
@@ -27,6 +37,15 @@ impl AuthenticationManager {
2737
if let Some(token) = token.filter(|token| !token.has_expired()) {
2838
return Ok(token);
2939
}
40+
41+
let _guard = self.refresh_mutex.lock().await;
42+
43+
// Check if refresh happened while we were waiting.
44+
let token = self.service_account.get_token(scopes);
45+
if let Some(token) = token.filter(|token| !token.has_expired()) {
46+
return Ok(token);
47+
}
48+
3049
self.service_account
3150
.refresh_token(&self.client, scopes)
3251
.await

src/lib.rs

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -111,31 +111,28 @@ async fn get_authentication_manager(
111111
};
112112

113113
if let Ok(service_account) = custom {
114-
return Ok(AuthenticationManager {
114+
return Ok(AuthenticationManager::new(
115115
client,
116-
service_account: Box::new(service_account),
117-
});
116+
Box::new(service_account),
117+
));
118118
}
119119
let gcloud = gcloud_authorized_user::GCloudAuthorizedUser::new().await;
120120
if let Ok(service_account) = gcloud {
121-
return Ok(AuthenticationManager {
122-
client: client.clone(),
123-
service_account: Box::new(service_account),
124-
});
121+
return Ok(AuthenticationManager::new(
122+
client.clone(),
123+
Box::new(service_account),
124+
));
125125
}
126126
let default = default_service_account::DefaultServiceAccount::new(&client).await;
127127
if let Ok(service_account) = default {
128-
return Ok(AuthenticationManager {
129-
client: client.clone(),
130-
service_account: Box::new(service_account),
131-
});
128+
return Ok(AuthenticationManager::new(
129+
client.clone(),
130+
Box::new(service_account),
131+
));
132132
}
133133
let user = default_authorized_user::DefaultAuthorizedUser::new(&client).await;
134134
if let Ok(user_account) = user {
135-
return Ok(AuthenticationManager {
136-
client,
137-
service_account: Box::new(user_account),
138-
});
135+
return Ok(AuthenticationManager::new(client, Box::new(user_account)));
139136
}
140137
Err(Error::NoAuthMethod(
141138
Box::new(custom.unwrap_err()),

0 commit comments

Comments
 (0)