Skip to main content

llm_optimizer_processor/storage/
manager.rs

1//! Storage Manager
2//!
3//! Coordinates multiple storage backends and provides unified access
4
5use async_trait::async_trait;
6use chrono::{DateTime, Utc};
7use serde::{de::DeserializeOwned, Serialize};
8use std::collections::HashMap;
9use std::sync::Arc;
10use tokio::sync::RwLock;
11use tracing::{debug, error, info, instrument, warn};
12
13use super::error::{StorageError, StorageResult};
14use super::traits::{Backup, BackupMetadata, Migrator, Migration, Storage};
15use super::types::{
16    Batch, Query, StorageBackend, StorageEntry, StorageHealth, StorageMetadata, StorageStats,
17    Transaction,
18};
19use super::wrapper::AnyStorage;
20
21/// Storage manager state
22#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23pub enum ManagerState {
24    Uninitialized,
25    Initializing,
26    Running,
27    Stopping,
28    Stopped,
29    Failed,
30}
31
32/// Storage manager statistics
33#[derive(Debug, Clone, Default)]
34pub struct ManagerStats {
35    /// Total operations across all backends
36    pub total_operations: u64,
37
38    /// Successful operations
39    pub successful_operations: u64,
40
41    /// Failed operations
42    pub failed_operations: u64,
43
44    /// Operations by backend
45    pub operations_by_backend: HashMap<String, u64>,
46
47    /// Fallback count (when primary backend fails)
48    pub fallback_count: u64,
49
50    /// Last updated
51    pub last_updated: DateTime<Utc>,
52}
53
54/// Storage routing strategy
55#[derive(Debug, Clone, Copy, PartialEq, Eq)]
56pub enum RoutingStrategy {
57    /// Route to primary backend only
58    PrimaryOnly,
59    /// Route reads to cache, writes to primary
60    CacheAside,
61    /// Write to multiple backends
62    MultiWrite,
63    /// Read from fastest available backend
64    FastestRead,
65}
66
67/// Storage manager coordinating multiple backends
68pub struct StorageManager {
69    /// Manager state
70    state: Arc<RwLock<ManagerState>>,
71
72    /// Primary storage backend
73    primary: Option<Arc<AnyStorage>>,
74
75    /// Cache backend (e.g., Redis)
76    cache: Option<Arc<AnyStorage>>,
77
78    /// Secondary/backup backend
79    secondary: Option<Arc<AnyStorage>>,
80
81    /// Available backends by type
82    backends: Arc<RwLock<HashMap<StorageBackend, Arc<AnyStorage>>>>,
83
84    /// Routing strategy
85    routing_strategy: RoutingStrategy,
86
87    /// Enable automatic fallback
88    enable_fallback: bool,
89
90    /// Manager statistics
91    stats: Arc<RwLock<ManagerStats>>,
92}
93
94impl StorageManager {
95    /// Create a new storage manager
96    pub fn new(routing_strategy: RoutingStrategy) -> Self {
97        Self {
98            state: Arc::new(RwLock::new(ManagerState::Uninitialized)),
99            primary: None,
100            cache: None,
101            secondary: None,
102            backends: Arc::new(RwLock::new(HashMap::new())),
103            routing_strategy,
104            enable_fallback: true,
105            stats: Arc::new(RwLock::new(ManagerStats::default())),
106        }
107    }
108
109    /// Set primary storage backend
110    pub fn with_primary(mut self, backend: Arc<AnyStorage>) -> Self {
111        let backend_type = backend.backend();
112        self.primary = Some(backend.clone());
113
114        // Store in backends map (need to do this after creation)
115        self.backends = Arc::new(RwLock::new({
116            let mut map = HashMap::new();
117            map.insert(backend_type, backend);
118            map
119        }));
120
121        self
122    }
123
124    /// Set cache backend
125    pub fn with_cache(mut self, backend: Arc<AnyStorage>) -> Self {
126        self.cache = Some(backend.clone());
127        self
128    }
129
130    /// Set secondary backend
131    pub fn with_secondary(mut self, backend: Arc<AnyStorage>) -> Self {
132        self.secondary = Some(backend);
133        self
134    }
135
136    /// Enable or disable automatic fallback
137    pub fn with_fallback(mut self, enable: bool) -> Self {
138        self.enable_fallback = enable;
139        self
140    }
141
142    /// Get current state
143    pub async fn state(&self) -> ManagerState {
144        *self.state.read().await
145    }
146
147    /// Get manager statistics
148    pub async fn manager_stats(&self) -> ManagerStats {
149        self.stats.read().await.clone()
150    }
151
152    /// Add a backend dynamically
153    #[instrument(skip(self, backend))]
154    pub async fn add_backend(&self, backend: Arc<AnyStorage>) -> StorageResult<()> {
155        let backend_type = backend.backend();
156        info!("Adding backend: {:?}", backend_type);
157
158        let mut backends = self.backends.write().await;
159        backends.insert(backend_type, backend);
160
161        Ok(())
162    }
163
164    /// Remove a backend
165    #[instrument(skip(self))]
166    pub async fn remove_backend(&self, backend_type: StorageBackend) -> StorageResult<()> {
167        info!("Removing backend: {:?}", backend_type);
168
169        let mut backends = self.backends.write().await;
170        backends.remove(&backend_type);
171
172        Ok(())
173    }
174
175    /// Get backend by type
176    pub async fn get_backend(
177        &self,
178        backend_type: StorageBackend,
179    ) -> Option<Arc<AnyStorage>> {
180        let backends = self.backends.read().await;
181        backends.get(&backend_type).cloned()
182    }
183
184    /// List all available backends
185    pub async fn list_backends(&self) -> Vec<StorageBackend> {
186        let backends = self.backends.read().await;
187        backends.keys().copied().collect()
188    }
189
190    /// Initialize all backends
191    #[instrument(skip(self))]
192    pub async fn initialize(&mut self) -> StorageResult<()> {
193        info!("Initializing storage manager");
194        *self.state.write().await = ManagerState::Initializing;
195
196        // Initialize primary backend
197        if let Some(ref mut primary) = self.primary {
198            if let Some(storage) = Arc::get_mut(primary) {
199                storage.initialize().await?;
200            }
201        }
202
203        // Initialize cache backend
204        if let Some(ref mut cache) = self.cache {
205            if let Some(storage) = Arc::get_mut(cache) {
206                storage.initialize().await?;
207            }
208        }
209
210        // Initialize secondary backend
211        if let Some(ref mut secondary) = self.secondary {
212            if let Some(storage) = Arc::get_mut(secondary) {
213                storage.initialize().await?;
214            }
215        }
216
217        *self.state.write().await = ManagerState::Running;
218        info!("Storage manager initialized successfully");
219
220        Ok(())
221    }
222
223    /// Shutdown all backends
224    #[instrument(skip(self))]
225    pub async fn shutdown(&mut self) -> StorageResult<()> {
226        info!("Shutting down storage manager");
227        *self.state.write().await = ManagerState::Stopping;
228
229        // Shutdown all backends
230        if let Some(ref mut primary) = self.primary {
231            if let Some(storage) = Arc::get_mut(primary) {
232                storage.shutdown().await?;
233            }
234        }
235
236        if let Some(ref mut cache) = self.cache {
237            if let Some(storage) = Arc::get_mut(cache) {
238                storage.shutdown().await?;
239            }
240        }
241
242        if let Some(ref mut secondary) = self.secondary {
243            if let Some(storage) = Arc::get_mut(secondary) {
244                storage.shutdown().await?;
245            }
246        }
247
248        *self.state.write().await = ManagerState::Stopped;
249        info!("Storage manager shut down successfully");
250
251        Ok(())
252    }
253
254    /// Execute operation with fallback
255    async fn execute_with_fallback<F, T>(&self, operation: F) -> StorageResult<T>
256    where
257        F: Fn(Arc<AnyStorage>) -> futures::future::BoxFuture<'static, StorageResult<T>> + Send,
258        T: Send,
259    {
260        // Try primary backend
261        if let Some(ref primary) = self.primary {
262            match operation(primary.clone()).await {
263                Ok(result) => {
264                    self.record_success(primary.backend()).await;
265                    return Ok(result);
266                }
267                Err(e) if !self.enable_fallback => {
268                    self.record_failure(primary.backend()).await;
269                    return Err(e);
270                }
271                Err(e) => {
272                    warn!("Primary backend failed: {}, trying fallback", e);
273                    self.record_failure(primary.backend()).await;
274                }
275            }
276        }
277
278        // Try secondary backend as fallback
279        if let Some(ref secondary) = self.secondary {
280            match operation(secondary.clone()).await {
281                Ok(result) => {
282                    self.record_success(secondary.backend()).await;
283                    self.record_fallback().await;
284                    return Ok(result);
285                }
286                Err(e) => {
287                    error!("Secondary backend also failed: {}", e);
288                    self.record_failure(secondary.backend()).await;
289                    return Err(e);
290                }
291            }
292        }
293
294        Err(StorageError::BackendNotAvailable {
295            backend: "all".to_string(),
296        })
297    }
298
299    /// Record successful operation
300    async fn record_success(&self, backend: StorageBackend) {
301        let mut stats = self.stats.write().await;
302        stats.total_operations += 1;
303        stats.successful_operations += 1;
304        *stats
305            .operations_by_backend
306            .entry(backend.name().to_string())
307            .or_insert(0) += 1;
308        stats.last_updated = Utc::now();
309    }
310
311    /// Record failed operation
312    async fn record_failure(&self, backend: StorageBackend) {
313        let mut stats = self.stats.write().await;
314        stats.total_operations += 1;
315        stats.failed_operations += 1;
316        stats.last_updated = Utc::now();
317    }
318
319    /// Record fallback usage
320    async fn record_fallback(&self) {
321        let mut stats = self.stats.write().await;
322        stats.fallback_count += 1;
323    }
324
325    /// Health check all backends
326    #[instrument(skip(self))]
327    pub async fn health_check_all(&self) -> HashMap<StorageBackend, StorageHealth> {
328        let mut results = HashMap::new();
329
330        let backends = self.backends.read().await;
331        for (backend_type, storage) in backends.iter() {
332            match storage.health_check().await {
333                Ok(health) => {
334                    results.insert(*backend_type, health);
335                }
336                Err(e) => {
337                    error!("Health check failed for {:?}: {}", backend_type, e);
338                    results.insert(*backend_type, StorageHealth::Unhealthy);
339                }
340            }
341        }
342
343        results
344    }
345
346    /// Get statistics from all backends
347    #[instrument(skip(self))]
348    pub async fn stats_all(&self) -> HashMap<StorageBackend, StorageStats> {
349        let mut results = HashMap::new();
350
351        let backends = self.backends.read().await;
352        for (backend_type, storage) in backends.iter() {
353            match storage.stats().await {
354                Ok(stats) => {
355                    results.insert(*backend_type, stats);
356                }
357                Err(e) => {
358                    error!("Failed to get stats for {:?}: {}", backend_type, e);
359                }
360            }
361        }
362
363        results
364    }
365}
366
367// Implement Debug for StorageManager
368impl std::fmt::Debug for StorageManager {
369    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
370        f.debug_struct("StorageManager")
371            .field("routing_strategy", &self.routing_strategy)
372            .field("has_primary", &self.primary.is_some())
373            .field("has_cache", &self.cache.is_some())
374            .field("has_secondary", &self.secondary.is_some())
375            .finish()
376    }
377}
378
379// Implement Storage trait for StorageManager
380#[async_trait]
381impl Storage for StorageManager {
382    fn backend(&self) -> StorageBackend {
383        if let Some(ref primary) = self.primary {
384            primary.backend()
385        } else {
386            StorageBackend::Sled // Default
387        }
388    }
389
390    async fn metadata(&self) -> StorageResult<StorageMetadata> {
391        if let Some(ref primary) = self.primary {
392            primary.metadata().await
393        } else {
394            Err(StorageError::BackendNotAvailable {
395                backend: "primary".to_string(),
396            })
397        }
398    }
399
400    async fn health_check(&self) -> StorageResult<StorageHealth> {
401        // Check if any backend is healthy
402        let health_results = self.health_check_all().await;
403
404        if health_results.values().any(|h| h == &StorageHealth::Healthy) {
405            Ok(StorageHealth::Healthy)
406        } else if health_results
407            .values()
408            .any(|h| h == &StorageHealth::Degraded)
409        {
410            Ok(StorageHealth::Degraded)
411        } else {
412            Ok(StorageHealth::Unhealthy)
413        }
414    }
415
416    async fn stats(&self) -> StorageResult<StorageStats> {
417        if let Some(ref primary) = self.primary {
418            primary.stats().await
419        } else {
420            Err(StorageError::BackendNotAvailable {
421                backend: "primary".to_string(),
422            })
423        }
424    }
425
426    #[instrument(skip(self, value))]
427    async fn insert<T: Serialize + DeserializeOwned + Send + Sync>(
428        &self,
429        table: &str,
430        key: &str,
431        value: &T,
432    ) -> StorageResult<()> {
433        match self.routing_strategy {
434            RoutingStrategy::CacheAside | RoutingStrategy::PrimaryOnly => {
435                // Write to primary only
436                let table = table.to_string();
437                let key = key.to_string();
438                let value_clone = serde_json::to_value(value).map_err(|e| {
439                    StorageError::SerializationError {
440                        source: Box::new(e),
441                    }
442                })?;
443
444                self.execute_with_fallback(|storage| {
445                    let table = table.clone();
446                    let key = key.clone();
447                    let value = value_clone.clone();
448                    Box::pin(async move {
449                        storage
450                            .insert(
451                                &table,
452                                &key,
453                                &serde_json::from_value::<T>(value).unwrap(),
454                            )
455                            .await
456                    })
457                })
458                .await
459            }
460            RoutingStrategy::MultiWrite => {
461                // Write to both primary and secondary
462                if let Some(ref primary) = self.primary {
463                    primary.insert(table, key, value).await?;
464                }
465                if let Some(ref secondary) = self.secondary {
466                    let _ = secondary.insert(table, key, value).await; // Best effort
467                }
468                Ok(())
469            }
470            RoutingStrategy::FastestRead => {
471                // Same as primary only for writes
472                if let Some(ref primary) = self.primary {
473                    primary.insert(table, key, value).await
474                } else {
475                    Err(StorageError::BackendNotAvailable {
476                        backend: "primary".to_string(),
477                    })
478                }
479            }
480        }
481    }
482
483    #[instrument(skip(self, value))]
484    async fn insert_with_ttl<T: Serialize + Send + Sync>(
485        &self,
486        table: &str,
487        key: &str,
488        value: &T,
489        ttl_seconds: i64,
490    ) -> StorageResult<()> {
491        // Write to cache if available, otherwise primary
492        if let Some(ref cache) = self.cache {
493            cache.insert_with_ttl(table, key, value, ttl_seconds).await
494        } else if let Some(ref primary) = self.primary {
495            primary
496                .insert_with_ttl(table, key, value, ttl_seconds)
497                .await
498        } else {
499            Err(StorageError::BackendNotAvailable {
500                backend: "any".to_string(),
501            })
502        }
503    }
504
505    #[instrument(skip(self))]
506    async fn get<T: DeserializeOwned + Serialize + Send + Sync>(
507        &self,
508        table: &str,
509        key: &str,
510    ) -> StorageResult<Option<StorageEntry<T>>> {
511        match self.routing_strategy {
512            RoutingStrategy::CacheAside => {
513                // Try cache first, then primary
514                if let Some(ref cache) = self.cache {
515                    if let Ok(Some(entry)) = cache.get(table, key).await {
516                        debug!("Cache hit for key: {}", key);
517                        return Ok(Some(entry));
518                    }
519                }
520
521                // Cache miss, get from primary
522                if let Some(ref primary) = self.primary {
523                    let entry = primary.get(table, key).await?;
524
525                    // Populate cache
526                    if let Some(ref cache) = self.cache {
527                        if let Some(ref e) = entry {
528                            let _ = cache.insert(table, key, &e.value).await;
529                        }
530                    }
531
532                    Ok(entry)
533                } else {
534                    Err(StorageError::BackendNotAvailable {
535                        backend: "primary".to_string(),
536                    })
537                }
538            }
539            _ => {
540                // Read from primary
541                if let Some(ref primary) = self.primary {
542                    primary.get(table, key).await
543                } else {
544                    Err(StorageError::BackendNotAvailable {
545                        backend: "primary".to_string(),
546                    })
547                }
548            }
549        }
550    }
551
552    #[instrument(skip(self, value))]
553    async fn update<T: Serialize + Send + Sync>(
554        &self,
555        table: &str,
556        key: &str,
557        value: &T,
558    ) -> StorageResult<()> {
559        if let Some(ref primary) = self.primary {
560            primary.update(table, key, value).await?;
561
562            // Invalidate cache
563            if let Some(ref cache) = self.cache {
564                let _ = cache.delete(table, key).await;
565            }
566
567            Ok(())
568        } else {
569            Err(StorageError::BackendNotAvailable {
570                backend: "primary".to_string(),
571            })
572        }
573    }
574
575    #[instrument(skip(self, value))]
576    async fn update_versioned<T: Serialize + Send + Sync>(
577        &self,
578        table: &str,
579        key: &str,
580        value: &T,
581        expected_version: u64,
582    ) -> StorageResult<()> {
583        if let Some(ref primary) = self.primary {
584            primary
585                .update_versioned(table, key, value, expected_version)
586                .await
587        } else {
588            Err(StorageError::BackendNotAvailable {
589                backend: "primary".to_string(),
590            })
591        }
592    }
593
594    #[instrument(skip(self))]
595    async fn delete(&self, table: &str, key: &str) -> StorageResult<()> {
596        if let Some(ref primary) = self.primary {
597            primary.delete(table, key).await?;
598
599            // Delete from cache
600            if let Some(ref cache) = self.cache {
601                let _ = cache.delete(table, key).await;
602            }
603
604            Ok(())
605        } else {
606            Err(StorageError::BackendNotAvailable {
607                backend: "primary".to_string(),
608            })
609        }
610    }
611
612    #[instrument(skip(self))]
613    async fn exists(&self, table: &str, key: &str) -> StorageResult<bool> {
614        if let Some(ref primary) = self.primary {
615            primary.exists(table, key).await
616        } else {
617            Err(StorageError::BackendNotAvailable {
618                backend: "primary".to_string(),
619            })
620        }
621    }
622
623    #[instrument(skip(self, batch))]
624    async fn execute_batch(&self, batch: Batch) -> StorageResult<()> {
625        if let Some(ref primary) = self.primary {
626            primary.execute_batch(batch).await
627        } else {
628            Err(StorageError::BackendNotAvailable {
629                backend: "primary".to_string(),
630            })
631        }
632    }
633
634    #[instrument(skip(self))]
635    async fn query<T: DeserializeOwned + Serialize + Send + Sync + 'static>(
636        &self,
637        query: Query,
638    ) -> StorageResult<Vec<StorageEntry<T>>> {
639        if let Some(ref primary) = self.primary {
640            primary.query(query).await
641        } else {
642            Err(StorageError::BackendNotAvailable {
643                backend: "primary".to_string(),
644            })
645        }
646    }
647
648    #[instrument(skip(self))]
649    async fn count(&self, query: Query) -> StorageResult<usize> {
650        if let Some(ref primary) = self.primary {
651            primary.count(query).await
652        } else {
653            Err(StorageError::BackendNotAvailable {
654                backend: "primary".to_string(),
655            })
656        }
657    }
658
659    #[instrument(skip(self))]
660    async fn begin_transaction(&self) -> StorageResult<Transaction> {
661        if let Some(ref primary) = self.primary {
662            primary.begin_transaction().await
663        } else {
664            Err(StorageError::BackendNotAvailable {
665                backend: "primary".to_string(),
666            })
667        }
668    }
669
670    #[instrument(skip(self, transaction))]
671    async fn commit_transaction(&self, transaction: Transaction) -> StorageResult<()> {
672        if let Some(ref primary) = self.primary {
673            primary.commit_transaction(transaction).await
674        } else {
675            Err(StorageError::BackendNotAvailable {
676                backend: "primary".to_string(),
677            })
678        }
679    }
680
681    #[instrument(skip(self, transaction))]
682    async fn rollback_transaction(&self, transaction: Transaction) -> StorageResult<()> {
683        if let Some(ref primary) = self.primary {
684            primary.rollback_transaction(transaction).await
685        } else {
686            Err(StorageError::BackendNotAvailable {
687                backend: "primary".to_string(),
688            })
689        }
690    }
691
692    #[instrument(skip(self))]
693    async fn scan_prefix(&self, table: &str, prefix: &str) -> StorageResult<Vec<String>> {
694        if let Some(ref primary) = self.primary {
695            primary.scan_prefix(table, prefix).await
696        } else {
697            Err(StorageError::BackendNotAvailable {
698                backend: "primary".to_string(),
699            })
700        }
701    }
702
703    #[instrument(skip(self))]
704    async fn get_multi<T: DeserializeOwned + Serialize + Send + Sync>(
705        &self,
706        table: &str,
707        keys: Vec<String>,
708    ) -> StorageResult<Vec<Option<StorageEntry<T>>>> {
709        if let Some(ref primary) = self.primary {
710            primary.get_multi(table, keys).await
711        } else {
712            Err(StorageError::BackendNotAvailable {
713                backend: "primary".to_string(),
714            })
715        }
716    }
717
718    #[instrument(skip(self))]
719    async fn delete_multi(&self, table: &str, keys: Vec<String>) -> StorageResult<()> {
720        if let Some(ref primary) = self.primary {
721            primary.delete_multi(table, keys).await
722        } else {
723            Err(StorageError::BackendNotAvailable {
724                backend: "primary".to_string(),
725            })
726        }
727    }
728
729    #[instrument(skip(self, new_value))]
730    async fn compare_and_swap<T: Serialize + Send + Sync>(
731        &self,
732        table: &str,
733        key: &str,
734        expected_version: u64,
735        new_value: &T,
736    ) -> StorageResult<bool> {
737        if let Some(ref primary) = self.primary {
738            primary
739                .compare_and_swap(table, key, expected_version, new_value)
740                .await
741        } else {
742            Err(StorageError::BackendNotAvailable {
743                backend: "primary".to_string(),
744            })
745        }
746    }
747
748    #[instrument(skip(self))]
749    async fn get_modified_since<T: DeserializeOwned + Serialize + Send + Sync + 'static>(
750        &self,
751        table: &str,
752        since: DateTime<Utc>,
753    ) -> StorageResult<Vec<StorageEntry<T>>> {
754        if let Some(ref primary) = self.primary {
755            primary.get_modified_since(table, since).await
756        } else {
757            Err(StorageError::BackendNotAvailable {
758                backend: "primary".to_string(),
759            })
760        }
761    }
762
763    #[instrument(skip(self))]
764    async fn get_expiring_before<T: DeserializeOwned + Serialize + Send + Sync + 'static>(
765        &self,
766        table: &str,
767        before: DateTime<Utc>,
768    ) -> StorageResult<Vec<StorageEntry<T>>> {
769        if let Some(ref primary) = self.primary {
770            primary.get_expiring_before(table, before).await
771        } else {
772            Err(StorageError::BackendNotAvailable {
773                backend: "primary".to_string(),
774            })
775        }
776    }
777
778    #[instrument(skip(self))]
779    async fn cleanup_expired(&self, table: &str) -> StorageResult<usize> {
780        if let Some(ref primary) = self.primary {
781            primary.cleanup_expired(table).await
782        } else {
783            Err(StorageError::BackendNotAvailable {
784                backend: "primary".to_string(),
785            })
786        }
787    }
788
789    #[instrument(skip(self))]
790    async fn compact(&self) -> StorageResult<()> {
791        if let Some(ref primary) = self.primary {
792            primary.compact().await
793        } else {
794            Ok(())
795        }
796    }
797
798    #[instrument(skip(self))]
799    async fn flush(&self) -> StorageResult<()> {
800        if let Some(ref primary) = self.primary {
801            primary.flush().await
802        } else {
803            Ok(())
804        }
805    }
806
807    async fn initialize(&mut self) -> StorageResult<()> {
808        self.initialize().await
809    }
810
811    async fn shutdown(&mut self) -> StorageResult<()> {
812        self.shutdown().await
813    }
814}
815
816#[cfg(test)]
817mod tests {
818    use super::*;
819
820    #[tokio::test]
821    async fn test_manager_state() {
822        let manager = StorageManager::new(RoutingStrategy::PrimaryOnly);
823        assert_eq!(manager.state().await, ManagerState::Uninitialized);
824    }
825
826    #[tokio::test]
827    async fn test_routing_strategies() {
828        let strategies = vec![
829            RoutingStrategy::PrimaryOnly,
830            RoutingStrategy::CacheAside,
831            RoutingStrategy::MultiWrite,
832            RoutingStrategy::FastestRead,
833        ];
834
835        for strategy in strategies {
836            let manager = StorageManager::new(strategy);
837            assert_eq!(manager.routing_strategy, strategy);
838        }
839    }
840}