1use crate::multi_tenancy::types::{MultiTenancyError, MultiTenancyResult};
4use chrono::{DateTime, Utc};
5use serde::{Deserialize, Serialize};
6use std::collections::HashMap;
7use std::sync::{Arc, Mutex};
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
11pub enum ResourceType {
12 VectorCount,
14
15 StorageBytes,
17
18 MemoryBytes,
20
21 ApiCalls,
23
24 QueriesPerSecond,
26
27 IndexBuilds,
29
30 EmbeddingGenerations,
32
33 ConcurrentRequests,
35
36 BatchSize,
38
39 Custom(u32),
41}
42
43impl ResourceType {
44 pub fn name(&self) -> String {
46 match self {
47 Self::VectorCount => "vector_count".to_string(),
48 Self::StorageBytes => "storage_bytes".to_string(),
49 Self::MemoryBytes => "memory_bytes".to_string(),
50 Self::ApiCalls => "api_calls".to_string(),
51 Self::QueriesPerSecond => "queries_per_second".to_string(),
52 Self::IndexBuilds => "index_builds".to_string(),
53 Self::EmbeddingGenerations => "embedding_generations".to_string(),
54 Self::ConcurrentRequests => "concurrent_requests".to_string(),
55 Self::BatchSize => "batch_size".to_string(),
56 Self::Custom(id) => format!("custom_{}", id),
57 }
58 }
59}
60
61#[derive(Debug, Clone, Serialize, Deserialize)]
63pub struct ResourceQuota {
64 pub resource_type: ResourceType,
66
67 pub limit: Option<u64>,
69
70 pub soft_limit: Option<u64>,
72
73 pub time_window_secs: Option<u64>,
75}
76
77impl ResourceQuota {
78 pub fn new(resource_type: ResourceType, limit: u64) -> Self {
80 Self {
81 resource_type,
82 limit: Some(limit),
83 soft_limit: None,
84 time_window_secs: None,
85 }
86 }
87
88 pub fn unlimited(resource_type: ResourceType) -> Self {
90 Self {
91 resource_type,
92 limit: None,
93 soft_limit: None,
94 time_window_secs: None,
95 }
96 }
97
98 pub fn with_soft_limit(mut self, soft_limit: u64) -> Self {
100 self.soft_limit = Some(soft_limit);
101 self
102 }
103
104 pub fn with_time_window(mut self, window_secs: u64) -> Self {
106 self.time_window_secs = Some(window_secs);
107 self
108 }
109
110 pub fn exceeds_hard_limit(&self, value: u64) -> bool {
112 if let Some(limit) = self.limit {
113 value > limit
114 } else {
115 false
116 }
117 }
118
119 pub fn exceeds_soft_limit(&self, value: u64) -> bool {
121 if let Some(soft_limit) = self.soft_limit {
122 value > soft_limit
123 } else {
124 false
125 }
126 }
127}
128
129#[derive(Debug, Clone, Serialize, Deserialize)]
131pub struct QuotaLimits {
132 pub tenant_id: String,
134
135 pub quotas: HashMap<ResourceType, ResourceQuota>,
137
138 pub strict_enforcement: bool,
140}
141
142impl QuotaLimits {
143 pub fn new(tenant_id: impl Into<String>) -> Self {
145 Self {
146 tenant_id: tenant_id.into(),
147 quotas: HashMap::new(),
148 strict_enforcement: true,
149 }
150 }
151
152 pub fn free_tier(tenant_id: impl Into<String>) -> Self {
154 let mut limits = Self::new(tenant_id);
155 limits.set_quota(ResourceQuota::new(ResourceType::VectorCount, 10_000));
156 limits.set_quota(ResourceQuota::new(ResourceType::StorageBytes, 100_000_000)); limits.set_quota(ResourceQuota::new(ResourceType::ApiCalls, 1000).with_time_window(3600));
158 limits.set_quota(ResourceQuota::new(ResourceType::QueriesPerSecond, 10));
159 limits
160 }
161
162 pub fn pro_tier(tenant_id: impl Into<String>) -> Self {
164 let mut limits = Self::new(tenant_id);
165 limits.set_quota(ResourceQuota::new(ResourceType::VectorCount, 1_000_000));
166 limits.set_quota(ResourceQuota::new(
167 ResourceType::StorageBytes,
168 10_000_000_000,
169 )); limits
171 .set_quota(ResourceQuota::new(ResourceType::ApiCalls, 100_000).with_time_window(3600));
172 limits.set_quota(ResourceQuota::new(ResourceType::QueriesPerSecond, 100));
173 limits
174 }
175
176 pub fn enterprise_tier(tenant_id: impl Into<String>) -> Self {
178 let mut limits = Self::new(tenant_id);
179 limits.set_quota(ResourceQuota::unlimited(ResourceType::VectorCount));
180 limits.set_quota(ResourceQuota::unlimited(ResourceType::StorageBytes));
181 limits.set_quota(ResourceQuota::unlimited(ResourceType::ApiCalls));
182 limits.set_quota(ResourceQuota::unlimited(ResourceType::QueriesPerSecond));
183 limits
184 }
185
186 pub fn set_quota(&mut self, quota: ResourceQuota) {
188 self.quotas.insert(quota.resource_type, quota);
189 }
190
191 pub fn get_quota(&self, resource_type: ResourceType) -> Option<&ResourceQuota> {
193 self.quotas.get(&resource_type)
194 }
195
196 pub fn check_limit(&self, resource_type: ResourceType, value: u64) -> bool {
198 if let Some(quota) = self.get_quota(resource_type) {
199 !quota.exceeds_hard_limit(value)
200 } else {
201 true }
203 }
204}
205
206#[derive(Debug, Clone, Serialize, Deserialize)]
208pub struct QuotaUsage {
209 pub tenant_id: String,
211
212 pub usage: HashMap<ResourceType, u64>,
214
215 pub updated_at: DateTime<Utc>,
217}
218
219impl QuotaUsage {
220 pub fn new(tenant_id: impl Into<String>) -> Self {
222 Self {
223 tenant_id: tenant_id.into(),
224 usage: HashMap::new(),
225 updated_at: Utc::now(),
226 }
227 }
228
229 pub fn get(&self, resource_type: ResourceType) -> u64 {
231 *self.usage.get(&resource_type).unwrap_or(&0)
232 }
233
234 pub fn set(&mut self, resource_type: ResourceType, value: u64) {
236 self.usage.insert(resource_type, value);
237 self.updated_at = Utc::now();
238 }
239
240 pub fn increment(&mut self, resource_type: ResourceType, amount: u64) {
242 let current = self.get(resource_type);
243 self.set(resource_type, current + amount);
244 }
245
246 pub fn decrement(&mut self, resource_type: ResourceType, amount: u64) {
248 let current = self.get(resource_type);
249 if current >= amount {
250 self.set(resource_type, current - amount);
251 } else {
252 self.set(resource_type, 0);
253 }
254 }
255
256 pub fn reset(&mut self, resource_type: ResourceType) {
258 self.set(resource_type, 0);
259 }
260
261 pub fn reset_all(&mut self) {
263 self.usage.clear();
264 self.updated_at = Utc::now();
265 }
266}
267
268pub struct QuotaEnforcer {
270 limits: Arc<Mutex<HashMap<String, QuotaLimits>>>,
272
273 usage: Arc<Mutex<HashMap<String, QuotaUsage>>>,
275}
276
277impl QuotaEnforcer {
278 pub fn new() -> Self {
280 Self {
281 limits: Arc::new(Mutex::new(HashMap::new())),
282 usage: Arc::new(Mutex::new(HashMap::new())),
283 }
284 }
285
286 pub fn set_limits(&self, limits: QuotaLimits) -> MultiTenancyResult<()> {
288 let tenant_id = limits.tenant_id.clone();
289 self.limits
290 .lock()
291 .map_err(|e| MultiTenancyError::InternalError {
292 message: format!("Lock error: {}", e),
293 })?
294 .insert(tenant_id.clone(), limits);
295
296 let mut usage_map = self
298 .usage
299 .lock()
300 .map_err(|e| MultiTenancyError::InternalError {
301 message: format!("Lock error: {}", e),
302 })?;
303 usage_map
304 .entry(tenant_id.clone())
305 .or_insert_with(|| QuotaUsage::new(tenant_id));
306
307 Ok(())
308 }
309
310 pub fn check_quota(
312 &self,
313 tenant_id: &str,
314 resource_type: ResourceType,
315 amount: u64,
316 ) -> MultiTenancyResult<bool> {
317 let limits = self
318 .limits
319 .lock()
320 .map_err(|e| MultiTenancyError::InternalError {
321 message: format!("Lock error: {}", e),
322 })?;
323
324 let usage = self
325 .usage
326 .lock()
327 .map_err(|e| MultiTenancyError::InternalError {
328 message: format!("Lock error: {}", e),
329 })?;
330
331 if let Some(tenant_limits) = limits.get(tenant_id) {
332 if let Some(tenant_usage) = usage.get(tenant_id) {
333 let current = tenant_usage.get(resource_type);
334 let new_usage = current + amount;
335
336 return Ok(tenant_limits.check_limit(resource_type, new_usage));
337 }
338 }
339
340 Ok(true)
342 }
343
344 pub fn consume(
346 &self,
347 tenant_id: &str,
348 resource_type: ResourceType,
349 amount: u64,
350 ) -> MultiTenancyResult<()> {
351 if !self.check_quota(tenant_id, resource_type, amount)? {
353 return Err(MultiTenancyError::QuotaExceeded {
354 tenant_id: tenant_id.to_string(),
355 resource: resource_type.name(),
356 });
357 }
358
359 let mut usage = self
361 .usage
362 .lock()
363 .map_err(|e| MultiTenancyError::InternalError {
364 message: format!("Lock error: {}", e),
365 })?;
366
367 usage
368 .entry(tenant_id.to_string())
369 .or_insert_with(|| QuotaUsage::new(tenant_id))
370 .increment(resource_type, amount);
371
372 Ok(())
373 }
374
375 pub fn release(
377 &self,
378 tenant_id: &str,
379 resource_type: ResourceType,
380 amount: u64,
381 ) -> MultiTenancyResult<()> {
382 let mut usage = self
383 .usage
384 .lock()
385 .map_err(|e| MultiTenancyError::InternalError {
386 message: format!("Lock error: {}", e),
387 })?;
388
389 usage
390 .entry(tenant_id.to_string())
391 .or_insert_with(|| QuotaUsage::new(tenant_id))
392 .decrement(resource_type, amount);
393
394 Ok(())
395 }
396
397 pub fn get_usage(&self, tenant_id: &str) -> MultiTenancyResult<QuotaUsage> {
399 let usage = self
400 .usage
401 .lock()
402 .map_err(|e| MultiTenancyError::InternalError {
403 message: format!("Lock error: {}", e),
404 })?;
405
406 Ok(usage
407 .get(tenant_id)
408 .cloned()
409 .unwrap_or_else(|| QuotaUsage::new(tenant_id)))
410 }
411
412 pub fn reset_usage(&self, tenant_id: &str) -> MultiTenancyResult<()> {
414 let mut usage = self
415 .usage
416 .lock()
417 .map_err(|e| MultiTenancyError::InternalError {
418 message: format!("Lock error: {}", e),
419 })?;
420
421 if let Some(tenant_usage) = usage.get_mut(tenant_id) {
422 tenant_usage.reset_all();
423 }
424
425 Ok(())
426 }
427}
428
429impl Default for QuotaEnforcer {
430 fn default() -> Self {
431 Self::new()
432 }
433}
434
435pub struct RateLimiter {
437 buckets: Arc<Mutex<HashMap<String, TokenBucket>>>,
439}
440
441#[derive(Debug, Clone)]
443struct TokenBucket {
444 tokens: f64,
446
447 capacity: f64,
449
450 refill_rate: f64,
452
453 last_refill: DateTime<Utc>,
455}
456
457impl TokenBucket {
458 fn new(capacity: f64, refill_rate: f64) -> Self {
460 Self {
461 tokens: capacity,
462 capacity,
463 refill_rate,
464 last_refill: Utc::now(),
465 }
466 }
467
468 fn refill(&mut self) {
470 let now = Utc::now();
471 let elapsed = (now - self.last_refill).num_milliseconds() as f64 / 1000.0;
472 let new_tokens = elapsed * self.refill_rate;
473
474 self.tokens = (self.tokens + new_tokens).min(self.capacity);
475 self.last_refill = now;
476 }
477
478 fn try_consume(&mut self, amount: f64) -> bool {
480 self.refill();
481
482 if self.tokens >= amount {
483 self.tokens -= amount;
484 true
485 } else {
486 false
487 }
488 }
489}
490
491impl RateLimiter {
492 pub fn new() -> Self {
494 Self {
495 buckets: Arc::new(Mutex::new(HashMap::new())),
496 }
497 }
498
499 pub fn set_rate(
501 &self,
502 tenant_id: impl Into<String>,
503 requests_per_second: f64,
504 ) -> MultiTenancyResult<()> {
505 let bucket = TokenBucket::new(requests_per_second * 2.0, requests_per_second);
506
507 self.buckets
508 .lock()
509 .map_err(|e| MultiTenancyError::InternalError {
510 message: format!("Lock error: {}", e),
511 })?
512 .insert(tenant_id.into(), bucket);
513
514 Ok(())
515 }
516
517 pub fn allow_request(&self, tenant_id: &str) -> MultiTenancyResult<bool> {
519 let mut buckets = self
520 .buckets
521 .lock()
522 .map_err(|e| MultiTenancyError::InternalError {
523 message: format!("Lock error: {}", e),
524 })?;
525
526 if let Some(bucket) = buckets.get_mut(tenant_id) {
527 Ok(bucket.try_consume(1.0))
528 } else {
529 Ok(true)
531 }
532 }
533
534 pub fn allow_batch_request(
536 &self,
537 tenant_id: &str,
538 batch_size: usize,
539 ) -> MultiTenancyResult<bool> {
540 let mut buckets = self
541 .buckets
542 .lock()
543 .map_err(|e| MultiTenancyError::InternalError {
544 message: format!("Lock error: {}", e),
545 })?;
546
547 if let Some(bucket) = buckets.get_mut(tenant_id) {
548 Ok(bucket.try_consume(batch_size as f64))
549 } else {
550 Ok(true)
552 }
553 }
554}
555
556impl Default for RateLimiter {
557 fn default() -> Self {
558 Self::new()
559 }
560}
561
562#[cfg(test)]
563mod tests {
564 use super::*;
565 use std::thread;
566 use std::time::Duration as StdDuration;
567
568 #[test]
569 fn test_resource_quota() {
570 let quota = ResourceQuota::new(ResourceType::VectorCount, 1000);
571 assert_eq!(quota.limit, Some(1000));
572 assert!(!quota.exceeds_hard_limit(500));
573 assert!(quota.exceeds_hard_limit(1001));
574
575 let quota = quota.with_soft_limit(800);
576 assert!(!quota.exceeds_soft_limit(700));
577 assert!(quota.exceeds_soft_limit(900));
578 }
579
580 #[test]
581 fn test_quota_limits() {
582 let limits = QuotaLimits::free_tier("tenant1");
583 assert!(limits.get_quota(ResourceType::VectorCount).is_some());
584 assert!(limits.check_limit(ResourceType::VectorCount, 5000));
585 assert!(!limits.check_limit(ResourceType::VectorCount, 20000));
586 }
587
588 #[test]
589 fn test_quota_usage() {
590 let mut usage = QuotaUsage::new("tenant1");
591 assert_eq!(usage.get(ResourceType::VectorCount), 0);
592
593 usage.increment(ResourceType::VectorCount, 100);
594 assert_eq!(usage.get(ResourceType::VectorCount), 100);
595
596 usage.increment(ResourceType::VectorCount, 50);
597 assert_eq!(usage.get(ResourceType::VectorCount), 150);
598
599 usage.decrement(ResourceType::VectorCount, 30);
600 assert_eq!(usage.get(ResourceType::VectorCount), 120);
601
602 usage.reset(ResourceType::VectorCount);
603 assert_eq!(usage.get(ResourceType::VectorCount), 0);
604 }
605
606 #[test]
607 fn test_quota_enforcer() {
608 let enforcer = QuotaEnforcer::new();
609 let limits = QuotaLimits::free_tier("tenant1");
610 enforcer.set_limits(limits).unwrap();
611
612 assert!(enforcer
614 .check_quota("tenant1", ResourceType::VectorCount, 5000)
615 .unwrap());
616
617 enforcer
619 .consume("tenant1", ResourceType::VectorCount, 5000)
620 .unwrap();
621
622 assert!(enforcer
624 .check_quota("tenant1", ResourceType::VectorCount, 3000)
625 .unwrap());
626
627 assert!(!enforcer
629 .check_quota("tenant1", ResourceType::VectorCount, 10000)
630 .unwrap());
631
632 assert!(enforcer
634 .consume("tenant1", ResourceType::VectorCount, 10000)
635 .is_err());
636 }
637
638 #[test]
639 fn test_rate_limiter() {
640 let limiter = RateLimiter::new();
641 limiter.set_rate("tenant1", 10.0).unwrap(); assert!(limiter.allow_request("tenant1").unwrap());
645 assert!(limiter.allow_request("tenant1").unwrap());
646
647 assert!(limiter.allow_batch_request("tenant1", 5).unwrap());
649
650 for _ in 0..20 {
652 let _ = limiter.allow_request("tenant1");
653 }
654 assert!(!limiter.allow_request("tenant1").unwrap());
655
656 thread::sleep(StdDuration::from_millis(200));
658 assert!(limiter.allow_request("tenant1").unwrap());
659 }
660
661 #[test]
662 fn test_tier_quotas() {
663 let free = QuotaLimits::free_tier("tenant1");
664 let pro = QuotaLimits::pro_tier("tenant2");
665 let enterprise = QuotaLimits::enterprise_tier("tenant3");
666
667 assert!(free.check_limit(ResourceType::VectorCount, 5000));
669 assert!(!free.check_limit(ResourceType::VectorCount, 20000));
670
671 assert!(pro.check_limit(ResourceType::VectorCount, 500000));
673 assert!(!pro.check_limit(ResourceType::VectorCount, 2000000));
674
675 assert!(enterprise.check_limit(ResourceType::VectorCount, 10000000));
677 assert!(enterprise.check_limit(ResourceType::VectorCount, 100000000));
678 }
679}