Adds a Spring @Scheduled job (daily at 03:00) that deletes all events whose expiry_date is before CURRENT_DATE using a native SQL DELETE. RSVPs are cascade-deleted via the existing FK constraint. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
72 lines
4.5 KiB
Markdown
72 lines
4.5 KiB
Markdown
# Feature Specification: Auto-Delete Expired Events
|
|
|
|
**Feature Branch**: `013-auto-delete-expired`
|
|
**Created**: 2026-03-09
|
|
**Status**: Draft
|
|
**Input**: User description: "Delete events automatically after they expired"
|
|
|
|
## User Scenarios & Testing *(mandatory)*
|
|
|
|
### User Story 1 - Automatic Cleanup of Expired Events (Priority: P1)
|
|
|
|
As a self-hoster, I want expired events to be automatically deleted from the database so that personal data is removed without manual intervention and storage stays clean.
|
|
|
|
A scheduled background job periodically checks all events. Any event whose `expiryDate` is in the past gets permanently deleted — including all associated data (RSVPs, tokens). No user action is required; the system handles this autonomously.
|
|
|
|
**Why this priority**: This is the core and only feature — automated, hands-off cleanup of expired events. It directly supports the privacy promise of fete.
|
|
|
|
**Independent Test**: Can be fully tested by creating events with past expiry dates, triggering the cleanup job, and verifying the events are gone from the database.
|
|
|
|
**Acceptance Scenarios**:
|
|
|
|
1. **Given** an event with an `expiryDate` in the past, **When** the scheduled cleanup job runs, **Then** the event and all its associated data (RSVPs, tokens) are permanently deleted.
|
|
2. **Given** an event with an `expiryDate` in the future, **When** the scheduled cleanup job runs, **Then** the event remains untouched.
|
|
3. **Given** multiple expired events exist, **When** the cleanup job runs, **Then** all expired events are deleted in a single run.
|
|
4. **Given** no expired events exist, **When** the cleanup job runs, **Then** nothing is deleted and the job completes without error.
|
|
|
|
---
|
|
|
|
### Edge Cases
|
|
|
|
- What happens if the cleanup job fails mid-deletion (e.g., database connection lost)? Events that were not yet deleted remain and will be picked up in the next run. No partial state.
|
|
- What happens if the server was offline for an extended period? On the next run, all accumulated expired events are deleted — no special catch-up logic needed.
|
|
- What happens if an organizer is viewing their event page while it gets deleted? The page shows a "not found" state on next interaction. This is acceptable because the event has expired.
|
|
|
|
## Requirements *(mandatory)*
|
|
|
|
### Functional Requirements
|
|
|
|
- **FR-001**: System MUST automatically delete events whose `expiryDate` is before the current date/time.
|
|
- **FR-002**: When an event is deleted, all associated data (RSVPs, organizer tokens, event tokens) MUST be deleted as well (cascade delete).
|
|
- **FR-003**: The cleanup job MUST run on a recurring schedule (default: once daily).
|
|
- **FR-004**: The cleanup job MUST be idempotent — running it multiple times produces the same result.
|
|
- **FR-005**: The cleanup job MUST log how many events were deleted per run.
|
|
- **FR-006**: Events that have not yet expired MUST NOT be affected by the cleanup job.
|
|
|
|
### Key Entities
|
|
|
|
- **Event**: The primary entity being cleaned up. Has an `expiryDate` field that determines when it becomes eligible for deletion.
|
|
- **RSVP**: Associated guest responses linked to an event. Deleted when the parent event is deleted.
|
|
|
|
## Success Criteria *(mandatory)*
|
|
|
|
### Measurable Outcomes
|
|
|
|
- **SC-001**: Expired events are deleted within 24 hours of their expiry date without manual intervention.
|
|
- **SC-002**: Zero data residue — when an event is deleted, no orphaned RSVPs or tokens remain in the system.
|
|
- **SC-003**: The cleanup process completes without errors under normal operating conditions.
|
|
- **SC-004**: Non-expired events are never affected by the cleanup process.
|
|
|
|
## Clarifications
|
|
|
|
### Session 2026-03-09
|
|
|
|
- Q: How should deletion be implemented — application code (JPA) or direct database query? → A: Direct database query. A single native `DELETE FROM events WHERE expiry_date < CURRENT_DATE` is sufficient. The existing `ON DELETE CASCADE` on the RSVPs foreign key ensures associated data is removed automatically. The existing `idx_events_expiry_date` index ensures the query is performant.
|
|
|
|
## Assumptions
|
|
|
|
- The `expiryDate` field already exists on events and is auto-set to `eventDate + 7 days` (implemented in the previous feature).
|
|
- Cascade deletion of associated data (RSVPs, tokens) is handled at the database level via foreign key constraints (`ON DELETE CASCADE` on `fk_rsvps_event_id`, verified in migration `003-create-rsvps-table.xml`).
|
|
- The daily schedule is sufficient — there is no requirement for near-real-time deletion.
|
|
- No "grace period" or "soft delete" is needed — events are permanently removed once expired.
|