Skip to content

Conversation

@joanagmaia
Copy link
Contributor

@joanagmaia joanagmaia commented Sep 11, 2025

Changes proposed ✍️

What

  • Added gdpr script for questdb
    • Soft delete as we will deprecate this database by the end of this month. In addition, the memberId and username will be modified to a random value so that values cannot be identified.
  • Added gdpr script for tinybird
    • Use selective deletion for affected records
  • Simplify and update gdpr script for postgres
    • Only allow to delete by memberId. Update tables that need deletion.

Checklist ✅

  • Label appropriately with Feature, Improvement, or Bug.
  • Add screenshots to the PR description for relevant FE changes
  • New backend functionality has been unit-tested.
  • API documentation has been updated (if necessary) (see docs on API documentation).
  • Quality standards are met.

Note

Adds a Tinybird data erasure script and refactors the Postgres member erasure script to be memberId-only with confirmations, summaries, identity archiving, FK-safe deletions, and search/org re-sync.

  • Scripts:
    • Tinybird erasure (services/apps/data_sink_worker/src/bin/erase-members-data-tinybird.ts):
      • Uses Tinybird selective delete API to remove member data from activities, activityRelations, maintainersInternal, memberIdentities, and members.
      • Builds deletion summary (record counts), prints curl-equivalent requests, and requires per-step confirmation.
      • Handles special cases: delete activities by activityId (from activityRelations), maintainersInternal by identityId subquery.
    • Postgres erasure (services/apps/data_sink_worker/src/bin/erase-member.ts):
      • Simplified to accept only memberId; shows deletion summary and prompts for confirmation.
      • Archives memberIdentities to requestedForErasureMemberIdentities before deletion.
      • Clears activityRelations.objectMemberId/objectMemberUsername, deletes maintainersInternal first, then all member-related tables, then members.
      • Adds per-table confirmation and triggers search member removal and organization re-sync.

Written by Cursor Bugbot for commit c7bb8c2. This will update automatically on new commits. Configure here.

@joanagmaia joanagmaia marked this pull request as ready for review December 18, 2025 15:25
@github-actions
Copy link
Contributor

⚠️ Jira Issue Key Missing

Your PR title doesn't contain a Jira issue key. Consider adding it for better traceability.

Example:

  • feat: add user authentication (CM-123)
  • feat: add user authentication (IN-123)

Projects:

  • CM: Community Data Platform
  • IN: Insights

Please add a Jira issue key to your PR title.

1 similar comment
@github-actions
Copy link
Contributor

⚠️ Jira Issue Key Missing

Your PR title doesn't contain a Jira issue key. Consider adding it for better traceability.

Example:

  • feat: add user authentication (CM-123)
  • feat: add user authentication (IN-123)

Projects:

  • CM: Community Data Platform
  • IN: Insights

Please add a Jira issue key to your PR title.

@joanagmaia joanagmaia merged commit fccff3c into main Dec 18, 2025
17 checks passed
@joanagmaia joanagmaia deleted the chore/gdpr-scripts branch December 18, 2025 15:25
)
if (!proceedMaintainers) {
throw new Error('User cancelled deletion from maintainersInternal')
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Interactive prompts block database transaction with held locks

The function deleteMemberFromDb is called within store.transactionally() at line 142, but it contains multiple await promptConfirmation() calls that wait for user input. This holds database locks open for an unpredictable amount of time while waiting for human response, which can cause transaction timeouts, lock contention with other database operations, and potential deadlocks. Interactive prompts should happen before the transaction begins, not within it.

Additional Locations (2)

Fix in Cursor Fix in Web

if (!proceedTable) {
log.info(`Skipped deletion from ${table}`)
continue
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Inconsistent cancellation allows partial deletion in transaction

When a user cancels deletion from a specific table during the loop, the code uses continue to skip to the next table while remaining in the same transaction. However, cancelling maintainersInternal (line 310) or the final member record (line 377) throws an error that rolls back the transaction. This inconsistency means a user could skip some table deletions while others proceed, resulting in partial data deletion within a committed transaction. For GDPR compliance, deletions should be all-or-nothing.

Fix in Cursor Fix in Web


if (orgResults.length > 0) {
for (const orgResult of orgResults) {
if (orgResult.organizationId) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Deletion summary omits activities table that gets deleted

The tablesToDelete map in getDeletionSummary does not include the activities table, but the same map in deleteMemberFromDb includes ['activities', ['memberId']] at line 327. This means the user confirmation summary won't show the activities that will be deleted, but the deletion will actually occur. Users receive misleading information about what data will be erased.

Additional Locations (1)

Fix in Cursor Fix in Web

if (orgDataMap.has(eIdentity.memberId)) {
orgResults = orgDataMap.get(eIdentity.memberId)
} else {
orgResults = await store
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Script exits with success code even after failure

When the deletion fails and an error is caught at line 154-155, the script logs the error but then unconditionally calls process.exit(0) at line 158, indicating success. For GDPR compliance scripts, failed deletions must be clearly indicated to automation and monitoring systems. The script should exit with a non-zero code when an error occurs.

Fix in Cursor Fix in Web

}
} catch (err) {
console.error(err)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Tinybird deletion continues after errors causing partial deletion

When a deletion from any Tinybird datasource fails, the error is caught and logged but the loop continues to process remaining tables. This can result in partial data deletion where some tables have the member's data removed while others retain it. For GDPR compliance, if any deletion fails (e.g., maintainersInternal fails but memberIdentities succeeds), the operation should abort rather than leave data in an inconsistent state. The script also exits with code 0 regardless of errors.

Fix in Cursor Fix in Web

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants