Repository navigation
Description
What happens
On a dynamic-schema table (schemaDefined: false), an operations-API write adds its new attributes to the table before the record write is staged. If the write is then refused or fails, the record never lands but the new indexed attributes stay.
Concretely, an insert/upsert/update/put with replicatedConfirmation that is refused before submission (a 400 residency refusal or a 503 feasibility refusal) leaves the table with 0 rows from that request and with every new attribute from the payload added to Table.attributes, written to the attribute descriptors, and indexed (Table.indices[name] is opened). The same leak happens for any later failure in the same loop, such as a validation, permission or source error on a record.
Impact is low: schema and index clutter, no row loss. It is worth tracking because the dynamic-schema attribute list is persistent and each leaked attribute costs an index store.
Where in the code
On origin/main e187b83d2:
dataLayer/harperBridge/ResourceBridge.ts:289-311(upsertRecords): insidetransaction(context, ...),if (!Table.schemaDefined)collectsnew_attributesfrom the payload and runsawait Table.addAttributes(...)(:301) before the record loop that callsTable.create/patch/put.resources/Table.ts:7927addAttributesgoes throughdeclareTableinresources/databases.ts, which changesTable.attributesin place, opens the index store, persists the descriptor, and attachesTable.indices[name], all outside the dataDatabaseTransaction.- The refusals are raised afterwards:
confirmingResidents(400,resources/Table.ts:5444) andDatabaseTransaction.refuseUnconfirmable(503,resources/DatabaseTransaction.ts:1919).
resources/DESIGN.md:982 states that a refused write never lands. That holds for the record (#3148 added the refusal). This issue is about the schema side effect of the same request, which that invariant does not cover.
Repro
qa1868-replication-confirmation-outcome.test.ts (QA candidate P-706), subtests 2a and 2b: send an insert with a new attribute and a confirmation request that is refused, then check describe_table. Measured on harper ea43e5677, RocksDB and LMDB; the ordering was re-read on e187b83d2 and is unchanged. Not re-run.
Suggested fix
There is no clean small fix, which is why this is P3 rather than a quick change:
- Deferring
addAttributesuntil after the write stages is not possible, because an indexed attribute has to exist before the record can be staged with its index entry. - A pre-check for the confirmation refusals would need the residency of the merged record, which is only known at commit.
Reasonable options are to roll back attributes this request added if the enclosing transaction aborts before commit (and nothing else used them in the meantime), or to document that dynamic-schema attribute creation is not transactional and leave it. Tables with schemaDefined: true do not auto-create attributes and are not affected.
Does not prove
Only ResourceBridge.upsertRecords (the operations-API write paths) was tested. Other write paths to dynamic-schema tables were not checked.
🤖 Prepared by finding-triage wave 37 (Claude Sonnet 5.5)
Metadata
Metadata
Assignees
Labels
Type
Fields
Priority
Activity