// monorepo/cloud/maplefile-backend/internal/maplefile/repo/storageusageevent/delete.go package storageusageevent import ( "context" "fmt" "time" "github.com/gocql/gocql" "go.uber.org/zap" ) func (impl *storageUsageEventRepositoryImpl) DeleteByUserAndDay(ctx context.Context, userID gocql.UUID, eventDay time.Time) error { // Ensure event day is truncated to date only eventDay = eventDay.Truncate(24 * time.Hour) query := `DELETE FROM maplefile.storage_usage_events_by_user_id_and_event_day_with_asc_event_time WHERE user_id = ? AND event_day = ?` err := impl.Session.Query(query, userID, eventDay).WithContext(ctx).Exec() if err != nil { impl.Logger.Error("failed to delete storage usage events by user and day", zap.Error(err)) return fmt.Errorf("failed to delete storage usage events: %w", err) } return nil } // DeleteByUserID deletes all storage usage events for a user (all days) // Used for GDPR right-to-be-forgotten implementation // // NOTE: Because storage_usage_events table is partitioned by (user_id, event_day), // we need to query to find all event_day values first, then delete each partition. // For efficiency, we'll delete up to 2 years of data (should cover most reasonable usage). func (impl *storageUsageEventRepositoryImpl) DeleteByUserID(ctx context.Context, userID gocql.UUID) error { // Delete events from the last 2 years (730 days) // This should cover all reasonable user data retention periods endDay := time.Now().Truncate(24 * time.Hour) startDay := endDay.Add(-730 * 24 * time.Hour) // 2 years ago impl.Logger.Info("Deleting storage usage events for user", zap.String("user_id", userID.String()), zap.Time("start_day", startDay), zap.Time("end_day", endDay)) // Use batch delete for efficiency batch := impl.Session.NewBatch(gocql.LoggedBatch).WithContext(ctx) deletedDays := 0 // Delete each day's partition for day := startDay; !day.After(endDay); day = day.Add(24 * time.Hour) { query := `DELETE FROM maplefile.storage_usage_events_by_user_id_and_event_day_with_asc_event_time WHERE user_id = ? AND event_day = ?` batch.Query(query, userID, day) deletedDays++ // Execute batch every 100 days to avoid batch size limits if deletedDays%100 == 0 { if err := impl.Session.ExecuteBatch(batch); err != nil { impl.Logger.Error("failed to execute batch delete for storage usage events", zap.String("user_id", userID.String()), zap.Int("days_in_batch", 100), zap.Error(err)) return fmt.Errorf("failed to delete storage usage events for user %s: %w", userID.String(), err) } // Create new batch for next set of days batch = impl.Session.NewBatch(gocql.LoggedBatch).WithContext(ctx) } } // Execute remaining batch if batch.Size() > 0 { if err := impl.Session.ExecuteBatch(batch); err != nil { impl.Logger.Error("failed to execute final batch delete for storage usage events", zap.String("user_id", userID.String()), zap.Int("days_in_final_batch", batch.Size()), zap.Error(err)) return fmt.Errorf("failed to delete storage usage events for user %s: %w", userID.String(), err) } } impl.Logger.Info("✅ Deleted all storage usage events for user", zap.String("user_id", userID.String()), zap.Int("total_days_deleted", deletedDays)) return nil }