monorepo/cloud/maplefile-backend/internal/repo/storageusageevent/delete.go

87 lines
3.2 KiB
Go

// 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
}