Skip to content

driver-memory: bulkUpdate and bulkDelete are still Promise.all(map(...)), so a refused row leaves the earlier rows of the batch applied — the third and fourth batch doors of the driver #13340 did not reach #13435

Description

@zhuangjianguo

Found while implementing #13340 (which made bulkCreate all-or-nothing). Not folded into that PR — different methods, and the fix is not the mechanical one #13340 got, for the reason set out below. Unassigned; grading and routing are triage's.

The observation

#13340 fixed bulkCreate, and #13197 had already fixed updateMany. Two batch doors of InMemoryDriver are still the pre-#13340 shape, in packages/drivers/driver-memory/src/memory-driver.ts (line numbers against c4e841368b, the #13340 branch head — re-derive them, they move):

// ~line 741 -- "Compatibility aliases"asyncbulkUpdate(object,updates,options){constresults=awaitPromise.all(updates.map(u=>this.update(object,u.id,u.data,options)));returnresults;}asyncbulkDelete(object,ids,options){awaitPromise.all(ids.map(id=>this.delete(object,id,options)));}

update writes into the table synchronously and calls assertUnique, so bulkUpdate has exactly the defect #13340 measured on bulkCreate: when one row of the batch is refused with UNIQUE_VIOLATION / 409, every row applied before it stays mutated, and the caller gets a rejection describing a batch that partly landed. bulkDelete is the same shape against delete, which throws on a missing row when strictMode is on.

So after #13340 the driver has four batch doors giving two different answers to "is a batch atomic?":

doorposturesince
updateManycheck-then-mutate#13197
bulkCreatecheck-then-push#13340
bulkUpdatePromise.all(map(update)) — prefix survives
bulkDeletePromise.all(map(delete)) — prefix survives

Why this was NOT folded into #13340

It fails the bounded-in-place-fix test on one criterion: the fix is not mechanical, so copying updateMany's shape into it would be forcing the pattern rather than transferring it. Three things have to be decided first, and none of them is settled by existing evidence:

  1. bulkUpdate applies a DIFFERENT patch per id.updateMany stamps one data onto every matched row, and bulkCreate has no pre-image to exclude. bulkUpdate needs, per pending row, its own exceptIdand a projected row set holding the other rows' post-images while dropping their pre-images. That construction exists nowhere in the file yet.
  2. Non-strict missing rows.update returns null rather than throwing when the id is absent and strictMode is off. Check-then-mutate has to decide whether a missing id refuses the whole batch or is skipped, which changes what the returned array means to callers.
  3. bulkDelete returns void and swallows per-row outcomes entirely, so "atomic delete" needs a decision about what it reports before it can be about what it does.

These are semantics questions, not a transcription. #13340's dispatch was explicit that if the updateMany pattern needs something the target method does lazily, that is a finding to report rather than a shape to force — this is that case, one method further on.

Scope note

Also worth deciding as part of this: bulkCreate/bulkUpdate/bulkDelete are labelled "Compatibility aliases" in the source. If they are genuinely legacy surface, "these three are not atomic, use the non-alias doors" is a legitimate answer — it is simply not the answer anywhere in the tree today, and the docstring on InMemoryDriver now advertises batch atomicity in general terms after #13340, which reads as covering all four doors.

Where it is

packages/drivers/driver-memory/src/memory-driver.tsbulkUpdate and bulkDelete, with bulkCreate (post-#13340) and updateMany in the same file as the two proven patterns.

Related

#13340 (made bulkCreate all-or-nothing; found there) · #13197 / #13239 (the uniqueness cards; updateMany's check-before-mutate came from the first)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions