feat: add configurable retry mechanism for API requests
parent
92c16fdc3d
commit
5f1b30772b
|
@ -3,6 +3,7 @@ output_path = "output_keys.txt"
|
||||||
backup_path = "backup_keys.txt"
|
backup_path = "backup_keys.txt"
|
||||||
api_host = "https://generativelanguage.googleapis.com/"
|
api_host = "https://generativelanguage.googleapis.com/"
|
||||||
timeout_sec = 20
|
timeout_sec = 20
|
||||||
|
max_retries = 2
|
||||||
concurrency = 30
|
concurrency = 30
|
||||||
proxy = "http://username:password@host:port"
|
proxy = "http://username:password@host:port"
|
||||||
enable_multiplexing = true
|
enable_multiplexing = true
|
||||||
|
|
|
@ -39,6 +39,10 @@ struct Cli {
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
concurrency: Option<usize>,
|
concurrency: Option<usize>,
|
||||||
|
|
||||||
|
#[arg(short = 'r', long)]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
max_retries: Option<usize>,
|
||||||
|
|
||||||
#[arg(short = 'x', long)]
|
#[arg(short = 'x', long)]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
proxy: Option<Url>,
|
proxy: Option<Url>,
|
||||||
|
@ -66,6 +70,10 @@ pub struct KeyCheckerConfig {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub timeout_sec: u64,
|
pub timeout_sec: u64,
|
||||||
|
|
||||||
|
// Maximum number of retries for failed requests.
|
||||||
|
#[serde(default)]
|
||||||
|
pub max_retries: usize,
|
||||||
|
|
||||||
// Maximum number of concurrent requests.
|
// Maximum number of concurrent requests.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub concurrency: usize,
|
pub concurrency: usize,
|
||||||
|
@ -154,6 +162,7 @@ static DEFAULT_CONFIG: LazyLock<KeyCheckerConfig> = LazyLock::new(|| KeyCheckerC
|
||||||
api_host: Url::parse("https://generativelanguage.googleapis.com/").unwrap(),
|
api_host: Url::parse("https://generativelanguage.googleapis.com/").unwrap(),
|
||||||
timeout_sec: 15,
|
timeout_sec: 15,
|
||||||
concurrency: 50,
|
concurrency: 50,
|
||||||
|
max_retries: 2,
|
||||||
proxy: None,
|
proxy: None,
|
||||||
enable_multiplexing: true,
|
enable_multiplexing: true,
|
||||||
log_level: "info".to_string(),
|
log_level: "info".to_string(),
|
||||||
|
|
|
@ -4,7 +4,7 @@ use tokio::time::Duration;
|
||||||
use tracing::{debug, error, info, warn};
|
use tracing::{debug, error, info, warn};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
use crate::config::TEST_MESSAGE_BODY;
|
use crate::config::{KeyCheckerConfig, TEST_MESSAGE_BODY};
|
||||||
use crate::error::ValidatorError;
|
use crate::error::ValidatorError;
|
||||||
use crate::types::GeminiKey;
|
use crate::types::GeminiKey;
|
||||||
|
|
||||||
|
@ -12,27 +12,32 @@ pub async fn validate_key(
|
||||||
client: Client,
|
client: Client,
|
||||||
api_endpoint: impl IntoUrl,
|
api_endpoint: impl IntoUrl,
|
||||||
api_key: GeminiKey,
|
api_key: GeminiKey,
|
||||||
|
config: KeyCheckerConfig,
|
||||||
) -> Result<GeminiKey, ValidatorError> {
|
) -> Result<GeminiKey, ValidatorError> {
|
||||||
let api_endpoint = api_endpoint.into_url()?;
|
let api_endpoint = api_endpoint.into_url()?;
|
||||||
|
|
||||||
match send_test_request(client, &api_endpoint, api_key.clone()).await {
|
match send_test_request(client, &api_endpoint, api_key.clone(), config.max_retries).await {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
info!("SUCCESS - {}... - Valid key found", &api_key.as_ref()[..10]);
|
info!("SUCCESS - {}... - Valid key found", &api_key.as_ref()[..10]);
|
||||||
Ok(api_key)
|
Ok(api_key)
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => match e.status() {
|
||||||
match e.status() {
|
Some(
|
||||||
Some(StatusCode::UNAUTHORIZED | StatusCode::FORBIDDEN | StatusCode::TOO_MANY_REQUESTS) => {
|
StatusCode::UNAUTHORIZED | StatusCode::FORBIDDEN | StatusCode::TOO_MANY_REQUESTS,
|
||||||
warn!("INVALID - {}... - {}", &api_key.as_ref()[..10], ValidatorError::KeyInvalid);
|
) => {
|
||||||
Err(ValidatorError::KeyInvalid)
|
warn!(
|
||||||
}
|
"INVALID - {}... - {}",
|
||||||
_ => {
|
&api_key.as_ref()[..10],
|
||||||
let req_error = ValidatorError::ReqwestError(e);
|
ValidatorError::KeyInvalid
|
||||||
error!("ERROR- {}... - {}", &api_key.as_ref()[..10], req_error);
|
);
|
||||||
Err(req_error)
|
Err(ValidatorError::KeyInvalid)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
_ => {
|
||||||
|
let req_error = ValidatorError::ReqwestError(e);
|
||||||
|
error!("ERROR- {}... - {}", &api_key.as_ref()[..10], req_error);
|
||||||
|
Err(req_error)
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,11 +45,12 @@ async fn send_test_request(
|
||||||
client: Client,
|
client: Client,
|
||||||
api_endpoint: &Url,
|
api_endpoint: &Url,
|
||||||
key: GeminiKey,
|
key: GeminiKey,
|
||||||
|
max_retries: usize,
|
||||||
) -> Result<reqwest::Response, reqwest::Error> {
|
) -> Result<reqwest::Response, reqwest::Error> {
|
||||||
let retry_policy = ExponentialBuilder::default()
|
let retry_policy = ExponentialBuilder::default()
|
||||||
.with_max_times(3)
|
.with_max_times(max_retries)
|
||||||
.with_min_delay(Duration::from_secs(3))
|
.with_min_delay(Duration::from_secs(1))
|
||||||
.with_max_delay(Duration::from_secs(5));
|
.with_max_delay(Duration::from_secs(2));
|
||||||
|
|
||||||
(async || {
|
(async || {
|
||||||
let response = client
|
let response = client
|
||||||
|
|
|
@ -48,7 +48,14 @@ impl ValidationService {
|
||||||
|
|
||||||
// Create stream to validate keys concurrently
|
// Create stream to validate keys concurrently
|
||||||
let valid_keys_stream = stream
|
let valid_keys_stream = stream
|
||||||
.map(|key| validate_key(self.client.clone(), self.full_url.clone(), key))
|
.map(|key| {
|
||||||
|
validate_key(
|
||||||
|
self.client.clone(),
|
||||||
|
self.full_url.clone(),
|
||||||
|
key,
|
||||||
|
self.config.clone(),
|
||||||
|
)
|
||||||
|
})
|
||||||
.buffer_unordered(self.config.concurrency)
|
.buffer_unordered(self.config.concurrency)
|
||||||
.filter_map(|result| async { result.ok() });
|
.filter_map(|result| async { result.ok() });
|
||||||
pin_mut!(valid_keys_stream);
|
pin_mut!(valid_keys_stream);
|
||||||
|
@ -72,7 +79,6 @@ impl ValidationService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub async fn start_validation() -> Result<(), ValidatorError> {
|
pub async fn start_validation() -> Result<(), ValidatorError> {
|
||||||
let config = KeyCheckerConfig::load_config()?;
|
let config = KeyCheckerConfig::load_config()?;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue