Skip to content

feat(core): seal personal data in scheduled commands and dead letters, cancelled by forget - #221

Open
benatespina wants to merge 1 commit into
mainfrom
feat/command-personal-data
Open

benatespina wants to merge 1 commit into
mainfrom
feat/command-personal-data

Conversation

@benatespina

Copy link
Copy Markdown
Member

Closes the gap that forget left: the payloads of scheduled commands and of their dead letters were stored in the clear and out of its reach.

What changes

  • Marks on commands. A command's payload receives personal and person (CommandPayloadArgs), as an event's does. personal types the field as the schema gives it, never | null: a command whose person is forgotten never runs, so its handler keeps receiving T. A query's PayloadArgs still has none.
  • Sealed where stored. The pipeline seals the marked fields when it schedules a command with delay (with the key of the person it names, created on first use, before any transaction opens). Retries keep the sealed payload, and a dead letter inherits it with the entry's persons, so nothing is sealed twice and no key is created on the way to a dead letter. A command dispatched at once is unchanged.
  • Opened where run. The scheduler worker opens the payload before dispatching. One sealed under a key its person no longer holds (a forget that read the key just before the seal) is dropped unrun, without a dead letter, and logged. app.deadLetters shows payloads opened (forgotten fields as null) and refuses to retry a letter whose person was forgotten (DeadLetterNotRetriableError).
  • Forget. In its transaction, after the key, snapshots and remembered rejections, forget cancels the scheduled commands and then removes the dead letters whose persons name the person. PersonalDataForgotten now carries { personId, cancelledCommands: { commandId, type }[], removedDeadLetters: { id, commandType }[] }.
  • Race with the worker. A run and the settling of its claim are one transaction, so a run that forget overtakes finds its claim lost and writes nothing, events or dead letter. Cancelling before removing letters closes the PostgreSQL interleaving; a storage-transaction contract case races the two on every store.
  • Before the marks. A command scheduled before its fields were marked stays as written, runs as before and is beyond forget; the guide says so.
  • Storage. ScheduledCommand, ScheduleArgs and DeadLetter gain optional persons; Scheduler and DeadLetterStore gain forget(person), answering what they removed in list order. Both tables get a persons column, with a GIN (jsonb_path_ops) index in PostgreSQL next to fix: close what the review of idempotency, snapshots and personal data found #220's. The memory transaction removes at once and puts back on failure, as it does keys. byFirstFailure joins @bounda-dev/core/adapter/sql.
  • Generator. A command's +types PayloadArgs is core.CommandPayloadArgs; fixtures regenerated, test-types cover the marks, the handler's T, and the new payload shape.
  • Docs. The personal-data guide gets "In a command's payload", describes what forget cancels and the races, and its "does not reach" paragraph now names only what app code writes (logs, error messages) and unmarked payloads. Concept page, limitations, dead-letters guide and errors reference updated. The pending personal-data changeset gains a paragraph.

No migration for existing tables, per the no-production-users policy: a database created by an earlier release lacks the persons columns.

Checks

  • pnpm check green (Docker up for PostgreSQL), after rebasing on fix: close what the review of idempotency, snapshots and personal data found #220.
  • Stryker on the touched files: core 97.8 %, postgresql 95.2 % (survivors equivalent or pre-existing), cli 100 %.
  • /code-review applied: key cache per person in openCommandPayload, a test for a policy scheduling a delayed personal command, and the guide naming handler error messages among what forget does not reach.

…, cancelled by forget

A command's payload takes personal() and person() as an event's does.
Wherever the command is stored, scheduled with delay, retried or
dead-lettered, its marked fields are sealed with the key of the person
it names, and the handler still receives them as the schema gives them.
forget cancels the person's scheduled commands and removes their dead
letters in its transaction, and PersonalDataForgotten names them.

This branch has not been deployed

No deployments
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.

1 participant