Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/fix-bulk-insert-duplicate-keys.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@tanstack/db": patch
---

Fix bulk insert not detecting duplicate keys within the same batch. Previously, when inserting multiple items with the same key in a single bulk insert operation, later items would silently overwrite earlier ones. Now, a `DuplicateKeyError` is thrown when duplicate keys are detected within the same batch.
6 changes: 4 additions & 2 deletions packages/db/src/collection/mutations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,17 +163,19 @@ export class CollectionMutationsManager<

const items = Array.isArray(data) ? data : [data]
const mutations: Array<PendingMutation<TOutput>> = []
const keysInCurrentBatch = new Set<TKey>()

// Create mutations for each item
items.forEach((item) => {
// Validate the data against the schema if one exists
const validatedData = this.validateData(item, `insert`)

// Check if an item with this ID already exists in the collection
// Check if an item with this ID already exists in the collection or in the current batch
const key = this.config.getKey(validatedData)
if (this.state.has(key)) {
if (this.state.has(key) || keysInCurrentBatch.has(key)) {
throw new DuplicateKeyError(key)
}
keysInCurrentBatch.add(key)
const globalKey = this.generateGlobalKey(key, item)

const mutation: PendingMutation<TOutput, `insert`> = {
Expand Down
45 changes: 45 additions & 0 deletions packages/db/tests/collection.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -567,6 +567,51 @@ describe(`Collection`, () => {
}).not.toThrow()
})

it(`should not allow bulk inserting documents with duplicate IDs in the same batch`, async () => {
const collection = createCollection<{ id: number; value: string }>({
id: `bulk-duplicate-id-test`,
getKey: (item) => item.id,
startSync: true,
sync: {
sync: ({ begin, commit, markReady }) => {
begin()
commit()
markReady()
},
},
})

await collection.stateWhenReady()

const mutationFn = async () => {}
const tx = createTransaction({ mutationFn })

// Try to bulk insert documents with duplicate IDs within the same batch
expect(() => {
tx.mutate(() =>
collection.insert([
{ id: 1, value: `first` },
{ id: 1, value: `second` }, // Same ID - should throw
])
)
}).toThrow(DuplicateKeyError)

// Should be able to bulk insert documents with different IDs
const tx2 = createTransaction({ mutationFn })
expect(() => {
tx2.mutate(() =>
collection.insert([
{ id: 2, value: `first` },
{ id: 3, value: `second` },
])
)
}).not.toThrow()

// Verify both items were inserted
expect(collection.state.get(2)).toEqual({ id: 2, value: `first` })
expect(collection.state.get(3)).toEqual({ id: 3, value: `second` })
})

it(`should support operation handler functions`, async () => {
// Create mock handler functions
const onInsertMock = vi.fn()
Expand Down
Loading