MemberJunction / MemberJunction/MJ

Orphan sweep hard-deletes records and ignores DeleteBehavior

Open
#3,528 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
TSQL
Stars
29
Forks
6
Avg merge
2d 1h
Merged PRs (30d)
323

Description

## Issue

`DeleteOrphanedRecords` deletes physically regardless of the entity map's `DeleteBehavior`.

- `packages/Integration/engine/src/IntegrationEngine.ts:2072-2075` — called with no `DeleteBehavior` check
- `:2762` — `entity.Delete()` unconditionally

Contrast `DeleteRecord` at `:3734`/`:3750`, which **does** honour `DeleteBehavior` and soft-deletes
where configured. So the two delete paths disagree, and the one that ignores the setting is the
automatic one.

## Why it is dangerous

Scope is rows carrying a record-map entry for that `(CompanyIntegration, EntityID)` — but
`UpdateRecord` (`:3622-3635`) **writes a record-map row for any row matched by key fields**,
including rows that pre-existed and were never created by the integration. So a key-field match
onto existing data enrols it, and if the source later stops returning that ExternalID on a full
sync, that pre-existing row is hard-deleted.

This is mostly latent while entity maps point at connector-generated staging tables. It becomes
serious the moment a map targets a real business entity — mapping a connector at a shared
`Person`/`Organization` entity means a vendor-side deletion can hard-delete records the integration
did not create, with `DeleteBehavior: 'SoftDelete'` or `'DoNothing'` configured and ignored.

## Related

Soft-delete also silently no-ops when the target lacks the tombstone columns: `:3751-3757` sets
nothing, `entity.Save()` short-circuits on `!Dirty`, and the engine records a successful archive
that never happened.

## Environment

MJ 5.51.0.

Contributor guide

Open the contributing guide

Research direction

Start in packages/Integration/engine/src/IntegrationEngine.ts at DeleteOrphanedRecords (2072-2075) and entity.Delete() (2762), then compare DeleteRecord at 3734 and 3750. Trace UpdateRecord at 3622-3635 and the soft-delete path at 3751-3757; done means orphan cleanup consistently respects DeleteBehavior and does not report a soft-delete that made no change.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
backend, databases
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.