Key takeaway
A deletion is an operation with an identity. Treating it as an empty value can preserve a row that the source has removed.
Start with the receiving state
A change feed can deliver every visible message without producing the right current table. The receiving application must know whether each message creates, replaces or removes state. For deletions, the important test is not whether a log contains the word delete. It is whether the identified source row disappears from the intended receiving view and stays correctly reconciled after restart.
Debezium’s PostgreSQL documentation distinguishes a delete change event from a subsequent tombstone. The delete has an envelope describing the operation; its after field is null because the row no longer exists. The tombstone has the same key and a null message value. These are different representations with different roles. Neither should be confused with an ordinary nullable business field.
Work through a two-row fixture
In this hypothetical test, an approved baseline contains K17 and K28. The source deletes K17 and leaves K28 unchanged. The receiver should end with exactly K28. The fixture uses invented identifiers and no actual company feed.
One faulty normalizer discards every envelope whose after field is null before examining its operation. It therefore loses the delete instruction. Another receiver processes the delete envelope correctly and ignores the following tombstone only for its application-state reducer, under a documented contract. Both receivers may say they ignore nulls; only the second has demonstrated the required final state.
Keep the operations and outcomes in the acceptance record. Counting two transport records as two business deletions would be as misleading as ignoring both. The tombstone can be essential to the topic’s compaction behavior without constituting a second removal of the same business row.
| Illustrative stage | Input meaning | Expected receiving state |
|---|---|---|
| Baseline | K17 and K28 exist | K17; K28 |
| Delete envelope | op d; key K17; after null | K28 only |
| Following tombstone | Key K17; entire message value null | K28 only; no second business deletion |
| Faulty after-null filter | Delete discarded before operation review | K17 remains: fail |
A compacted topic is not an unlimited deletion history
Kafka documents compaction as retaining the latest value for a key while allowing older records to be removed. A keyed null-value record marks deletion, and the marker itself can later be cleaned out. The documented guarantee that a consumer sees delete markers depends on reaching the head within the topic’s configured delete-retention period.
Now extend the hypothetical fixture. A restored checkpoint still contains K17, but the consumer has been offline beyond the actual marker-retention boundary. Resuming that old state cannot be described as complete merely because no new K17 record appears. The absence of a retained message is not a fresh instruction that corrects the stale local row.
The recovery design needs an explicit reconciliation or rebuild route. A clean reconstruction from an appropriate empty state is a different operation from resuming an old materialized view. Record the source/baseline boundary, checkpoint age, retained-log coverage and resulting key set. Do not advertise a compacted stream as a complete immutable history of every earlier update.
Identify what the connector can actually name
The PostgreSQL connector’s replica-identity settings affect the old-row information available for update and delete events. A receiving design that expects the full previous row needs evidence that the source supplies it. An apparently adequate key in an example does not establish the key or before-image available from a particular table.
Keep the connector version, table identity rule, relevant transformations and tombstone configuration with the test. Review primary-key changes and table-wide operations separately if they are in the promised scope. A contract tested only on a simple row deletion cannot silently cover a renamed key, truncated table or changed serialization layer.
The same caution applies to recreation. If K17 is legitimately created again, the new accepted source operation needs its own correct effect. “Deleted once” is not a permanent instruction to suppress every future record with that key. General replay and ordering controls remain separate checks.
Accept the bounded result, retain the recovery gap
The small fixture passes when K28 is the only remaining row, the reducer distinguishes a nullable field from a deletion, and the restart case has a supported final-state reconciliation. A gap in retained markers remains an explicit hold on the stale checkpoint, rather than a missing-data value quietly filled with an assumption.
Use the diligence question builder to ask which operation representations and recovery paths were exercised. The inventory can identify whether the archive contains current rows, changes or both. This is a technical state-maintenance test. It does not establish legal erasure, removal from every downstream copy, or permission to share the underlying records.