// monorepo/cloud/maplefile-backend/internal/maplefile/repo/storageusageevent/get.go package storageusageevent import ( "context" "fmt" "time" "github.com/gocql/gocql" "go.uber.org/zap" "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/storageusageevent" ) func (impl *storageUsageEventRepositoryImpl) GetByUserAndDay(ctx context.Context, userID gocql.UUID, eventDay time.Time) ([]*storageusageevent.StorageUsageEvent, error) { // Ensure event day is truncated to date only eventDay = eventDay.Truncate(24 * time.Hour) query := `SELECT user_id, event_day, event_time, file_size, operation FROM maplefile.storage_usage_events_by_user_id_and_event_day_with_asc_event_time WHERE user_id = ? AND event_day = ?` iter := impl.Session.Query(query, userID, eventDay).WithContext(ctx).Iter() var events []*storageusageevent.StorageUsageEvent var ( resultUserID gocql.UUID resultEventDay time.Time eventTime time.Time fileSize int64 operation string ) for iter.Scan(&resultUserID, &resultEventDay, &eventTime, &fileSize, &operation) { event := &storageusageevent.StorageUsageEvent{ UserID: resultUserID, EventDay: resultEventDay, EventTime: eventTime, FileSize: fileSize, Operation: operation, } events = append(events, event) } if err := iter.Close(); err != nil { impl.Logger.Error("failed to get storage usage events by user and day", zap.Error(err)) return nil, fmt.Errorf("failed to get storage usage events: %w", err) } return events, nil } func (impl *storageUsageEventRepositoryImpl) GetByUserDateRange(ctx context.Context, userID gocql.UUID, startDay, endDay time.Time) ([]*storageusageevent.StorageUsageEvent, error) { // Ensure dates are truncated to date only startDay = startDay.Truncate(24 * time.Hour) endDay = endDay.Truncate(24 * time.Hour) // For better performance with large date ranges, we'll query in parallel var allEvents []*storageusageevent.StorageUsageEvent eventsChan := make(chan []*storageusageevent.StorageUsageEvent) errorsChan := make(chan error) // Calculate number of days days := int(endDay.Sub(startDay).Hours()/24) + 1 // Query each day in parallel (limit concurrency to avoid overwhelming Cassandra) concurrency := 10 if days < concurrency { concurrency = days } semaphore := make(chan struct{}, concurrency) daysProcessed := 0 for day := startDay; !day.After(endDay); day = day.Add(24 * time.Hour) { semaphore <- struct{}{} daysProcessed++ go func(queryDay time.Time) { defer func() { <-semaphore }() events, err := impl.GetByUserAndDay(ctx, userID, queryDay) if err != nil { errorsChan <- err return } eventsChan <- events }(day) } // Collect results var firstError error for i := 0; i < daysProcessed; i++ { select { case events := <-eventsChan: allEvents = append(allEvents, events...) case err := <-errorsChan: if firstError == nil { firstError = err } case <-ctx.Done(): return nil, ctx.Err() } } if firstError != nil { impl.Logger.Error("failed to get events for date range", zap.Error(firstError), zap.Int("days_requested", days)) return allEvents, firstError // Return partial results } return allEvents, nil } // Convenience methods for trend analysis func (impl *storageUsageEventRepositoryImpl) GetLast7DaysEvents(ctx context.Context, userID gocql.UUID) ([]*storageusageevent.StorageUsageEvent, error) { endDay := time.Now().Truncate(24 * time.Hour) startDay := endDay.Add(-6 * 24 * time.Hour) // 7 days including today return impl.GetByUserDateRange(ctx, userID, startDay, endDay) } func (impl *storageUsageEventRepositoryImpl) GetLastNDaysEvents(ctx context.Context, userID gocql.UUID, days int) ([]*storageusageevent.StorageUsageEvent, error) { if days <= 0 { return nil, fmt.Errorf("days must be positive") } endDay := time.Now().Truncate(24 * time.Hour) startDay := endDay.Add(-time.Duration(days-1) * 24 * time.Hour) return impl.GetByUserDateRange(ctx, userID, startDay, endDay) } func (impl *storageUsageEventRepositoryImpl) GetMonthlyEvents(ctx context.Context, userID gocql.UUID, year int, month time.Month) ([]*storageusageevent.StorageUsageEvent, error) { startDay := time.Date(year, month, 1, 0, 0, 0, 0, time.UTC) endDay := startDay.AddDate(0, 1, -1) // Last day of the month return impl.GetByUserDateRange(ctx, userID, startDay, endDay) } func (impl *storageUsageEventRepositoryImpl) GetYearlyEvents(ctx context.Context, userID gocql.UUID, year int) ([]*storageusageevent.StorageUsageEvent, error) { startDay := time.Date(year, 1, 1, 0, 0, 0, 0, time.UTC) endDay := time.Date(year, 12, 31, 0, 0, 0, 0, time.UTC) return impl.GetByUserDateRange(ctx, userID, startDay, endDay) }