# Cache Package ## Overview The `cache` package provides application-level caching for CV and UI data, eliminating per-request file I/O by loading all data once at application startup. This improves performance and reduces latency for all handler operations. **Key Benefits:** - Single load at startup, fast reads during request handling - Thread-safe concurrent access using `sync.RWMutex` - Language-keyed access ("en", "es") - Fast-fail strategy: fails at startup if any language data cannot be loaded ## Architecture ### DataCache Structure ```go type DataCache struct { cv map[string]*cvmodel.CV // CV data indexed by language ui map[string]*uimodel.UI // UI data indexed by language mu sync.RWMutex // Protects concurrent reads } ``` The cache stores pointer references to CV and UI models, loaded from YAML files. Since reads are frequent and writes never occur, `sync.RWMutex` provides efficient concurrent access. ## Usage ### Initialization The cache is created once at application startup in `main.go`: ```go // Initialize data cache (load CV and UI data once at startup) dataCache, err := cache.New([]string{"en", "es"}) if err != nil { log.Fatalf("Failed to initialize data cache: %v", err) } ``` This loads CV and UI data for English and Spanish. If any language fails to load, the entire startup fails—catch errors early rather than on first request. ### Handler Integration The cache is injected into handlers via constructor: ```go cvHandler := handlers.NewCVHandler(templateMgr, serverAddr, emailService, dataCache) ``` Handlers access cached data using language-specific getters: ```go func (h *CVHandler) renderPage(w http.ResponseWriter, r *http.Request) { lang := r.URL.Query().Get("lang") cv := h.dataCache.GetCV(lang) ui := h.dataCache.GetUI(lang) // Use cv and ui data for rendering... } ``` ## API Reference ### `New(languages []string) (*DataCache, error)` Creates and initializes a new cache with data for the specified languages. **Parameters:** - `languages`: List of language codes to load (e.g., `[]string{"en", "es"}`) **Returns:** - `*DataCache`: Initialized cache instance - `error`: Non-nil if any language fails to load **Behavior:** - Returns `nil` and error if any language's CV or UI data fails to load - Empty language list creates empty cache (no error) - Fails at startup rather than deferring errors to request time **Example:** ```go cache, err := cache.New([]string{"en", "es"}) if err != nil { log.Fatalf("Failed to initialize cache: %v", err) } ``` ### `GetCV(lang string) *cvmodel.CV` Retrieves cached CV data for the specified language. **Parameters:** - `lang`: Language code (e.g., "en", "es") **Returns:** - `*cvmodel.CV`: Pointer to CV data, or `nil` if language not found - **Note:** Callers must check for `nil` before dereferencing **Thread Safety:** Safe for concurrent reads **Example:** ```go cv := cache.GetCV("en") if cv == nil { // Handle missing language return fmt.Errorf("CV not available for language: en") } // Use cv... ``` ### `GetUI(lang string) *uimodel.UI` Retrieves cached UI data for the specified language. **Parameters:** - `lang`: Language code (e.g., "en", "es") **Returns:** - `*uimodel.UI`: Pointer to UI data, or `nil` if language not found **Thread Safety:** Safe for concurrent reads **Example:** ```go ui := cache.GetUI("es") if ui != nil { title := ui.Navigation.Title } ``` ### `Languages() []string` Returns all language codes currently cached. **Returns:** - `[]string`: Slice of available language codes (order not guaranteed) **Thread Safety:** Safe for concurrent reads **Example:** ```go langs := cache.Languages() for _, lang := range langs { cv := cache.GetCV(lang) // Process CV for each language... } ``` ## Mutating Cached Data ### Important: Deep Copies for Mutable Fields Since cache stores pointer references, handlers that modify CV slices must create deep copies before modification: ```go // In handlers that modify experience/projects: func prepareTemplateData(cv *cvmodel.CV) *cvmodel.CV { // Create copies of mutable slices copy := &cvmodel.CV{ Personal: cv.Personal, Experience: append([]cvmodel.Experience{}, cv.Experience...), // Deep copy Projects: append([]cvmodel.Project{}, cv.Projects...), // Deep copy Education: cv.Education, Skills: cv.Skills, } // Now safe to modify copy.Experience and copy.Projects for i := range copy.Experience { copy.Experience[i].YearsOfExperience = calculateYears() } return copy } ``` This prevents handlers from accidentally mutating cached data during request processing. ## Supported Languages Currently configured for: - `"en"` - English - `"es"` - Spanish To add a new language, update `main.go`: ```go dataCache, err := cache.New([]string{"en", "es", "fr"}) // Add "fr" ``` Ensure YAML data files exist in the data directory for the new language, or startup will fail. ## Error Handling ### Startup Failures The fast-fail strategy ensures all data issues are caught before the server starts: ```go dataCache, err := cache.New([]string{"en", "es"}) if err != nil { // Example error messages: // "load CV for 'fr': file not found" // "load UI for 'es': invalid YAML" log.Fatalf("Failed to initialize data cache: %v", err) } ``` ### Runtime Handling Handlers should gracefully handle missing languages: ```go cv := cache.GetCV(lang) if cv == nil { http.Error(w, "Language not supported", http.StatusNotFound) return } ``` ## Performance Considerations ### I/O Efficiency - **Single Load:** CV and UI YAML files are parsed once at startup - **No Per-Request I/O:** Handler requests never touch disk - **Memory Trade-off:** Stores decoded objects in memory ### Concurrency - **RWMutex:** Optimized for high read throughput, zero writes - **No Contention:** 100+ concurrent reads verified in tests - **Nil Returns:** Fast path for missing languages (map lookup only) ### Memory Usage - Minimal overhead: Two maps + one mutex - Proportional to number of languages loaded - Shared object references (no duplication per request) ## Testing Run the comprehensive test suite: ```bash go test ./internal/cache -v ``` Test coverage includes: - Cache initialization with valid/invalid languages - CV and UI data retrieval - Thread safety with concurrent reads - Data integrity verification - Empty language list handling ## Dependencies - `internal/models/cv` - CV data model - `internal/models/ui` - UI data model - Go standard library: `sync` ## Related Files - **`internal/cache/data_cache.go`** - Cache implementation - **`internal/cache/data_cache_test.go`** - Comprehensive test suite - **`main.go`** - Cache initialization at startup - **`internal/handlers/cv.go`** - Handler injection point