On this page
Record write reference
Look up how Trazadera Golden resolves the record a write addresses, what an upsert, a delete, an entity synchronization and a table load answer, and what an update changes.
Every write to an entity’s records follows the same rules for finding the record it addresses and for what an update changes. This page states those rules for a single-record upsert or delete and for an entity synchronization, and lists where a bulk load counts a row instead of refusing it.
| Operation | Writes | Role |
|---|---|---|
POST /api/golden/{entity}/upsert | One record of an entity | STEWARD or ADMIN |
DELETE /api/golden/{entity}/delete | Deletes one record of an entity | STEWARD or ADMIN |
PUT /api/entities/synchronize | An entity’s records, from its configured sources | ADMIN |
POST /api/tables/load, POST /api/tables/transform | A table that belongs to no entity | ADMIN |
An entity’s data is written only through the entity: its synchronization or the upsert. A table load or transform whose target is an entity’s table is refused; see Table loads.
The upsert request
The request carries one record and optional controls:
| Member | Default | Effect |
|---|---|---|
record | Required | The record to write. Its _id, when present, is read as a string, so 42 and "42" address the same record |
insert | false | Create a record; refuse the request when the key already names one |
update | false | Update an existing record; refuse the request when the key names none |
transformation | None | A transformation resource applied to record before the entity’s pipeline. A name that is not a transformation answers 404 |
comment | None | A comment recorded with the change where auditing is enabled |
Setting both insert and update is a 400.
The response reports inserted, updated, the written record, and
messages that explain any choice Golden made, such as an _id it ignored.
An update that changes nothing still answers updated: true, with a message
saying the record already held these values.
How the upsert finds its record
Golden computes the record’s identity from the body as the entity’s pipeline
leaves it, after any requested transformation. Two spellings of one key that
the pipeline brings to the same canonical value, such as b-123 and B123
under a cleaner that canonicalizes them, are one key. The identity comes from
the dataset identity:
DEFAULT derives none, COLUMN joins the identity columns, and SCRIPT
evaluates the identity script.
The key is resolved in this order:
- A live
_idaddresses the write, whatever its shape. - An
_idin history answers for itself:409withsurvivorIdwhen a merge absorbed it,409withrestoredIdswhen it was the survivor of a merge that was undone, or410when it was deleted. This holds with or withoutinsert: true, on every dataset identity type. - Otherwise, on a
COLUMNorSCRIPTdataset whose body yields a usable identity, the identity addresses the write. A live record is updated, a record in history answers as in step 2, and a free identity is created, so the new record’s_idequals its identity. An_idGolden never issued gives way to the identity, andmessagesnames it. - Otherwise an
_idthat names no record is a409saying to omit_idto create a record. - Otherwise the record is created under a new identifier. On a
COLUMNorSCRIPTdataset whose identity is absent or has a blank component,messagessays so, and that sending the same body again creates another record.
A merge survivor carries a new identifier by design and is written through
that _id; its derived identity is not a write address.
Situations and answers
| Situation | Answer |
|---|---|
DEFAULT dataset, an _id no record has | 409: no record has the id; omit _id to create a record |
No _id, an identity that names a live record (COLUMN or SCRIPT) | That record is updated |
An _id Golden never issued, beside an identity that names a live record | The identity’s record is updated; messages names the ignored _id and the identity used |
An _id Golden never issued and no identity in the body (COLUMN or SCRIPT) | 409 naming the dataset’s identity and saying it arrived empty |
insert: true with a live _id or a live identity | 409 with existingIds naming that record |
insert: true with a free identity | Created under the identity |
insert: true with an _id or identity in history | The same answer as without the flag: 409 with survivorId, 409 with restoredIds, or 410 |
insert: true with an _id Golden never issued, on DEFAULT or a COLUMN identity | The _id is not used as the address; messages names it and says what the record was created under |
insert: true on a SCRIPT identity that reads _id | The _id is not the address but is an input: the identity computed from it answers as above. A free one is created under it |
update: true and nothing found | 409 naming the key the request sent, or saying it sent none |
| The survivor of an undone merge | 409 with restoredIds naming the restored records that are live now; 410 when none is |
No _id, or an _id that names no record (or insert: true), with an identity value that cannot key a record | 400 naming the column or the identity script, and the reason. Nothing is written |
| An identity script that fails | 400 naming the failure. Nothing is written |
Identity values that cannot key a record
An identity value cannot key a record when it is longer than 255 characters,
contains /, \, ?, #, %, [, ] or a control character, or is . or
...
A COLUMN identity of several columns joins their trimmed values with ::.
No component may contain :: or start or end with :, so a key splits one
way only; the rule applies to a one-column identity too. A SCRIPT identity is
one value and is not split.
Read a conflict
A 409 from an upsert or a delete carries a conflict body. The member that is
filled says what to do next:
| Member | Meaning | Next step |
|---|---|---|
survivorId | A merge absorbed the record the request named | Write to the survivor |
restoredIds | The request named the survivor of a merge that was undone; these are the restored records that are live now | Choose the restored record to write to |
existingIds | A live record already holds the key or the source pair | Use that identifier |
existingIdsTotal | Accompanies existingIds: how many records hold it. It exceeds the length of existingIds only when the list was cut | — |
| None | The key the request named is wrong | Correct the request |
existingIds names at most 20 live records, sorted by identifier; when it is
cut, the message says how many of the total it names. A 410 means the
addressed record was deleted. See conflict handling
for the general status conventions.
What an update writes
An update is a partial merge:
- A member left out of the body is untouched.
- A member sent as
nullis emptied. - A member cannot be removed from the record, only emptied.
The same rule applies to an entity synchronization and a table load; see Empty values in a load for how each source format expresses “empty” and “untouched”.
Two parts of a record are fixed after creation:
- Provenance. An update’s
_sourcemay restate entries the record already has; they are compared trimmed and change nothing. A new or different entry is a400. - Raw content. An update carrying
_xml,_textor_binaryis a400naming those members; nothing is written.
A malformed _source is a 400 naming the first problem found, with the entry
and the member, in the caller’s language.
Golden manages _metadata, _audit, quality and search evidence itself; a
request carrying _audit is a 400. Build a write
from the record’s business fields and _id rather than from a complete read
response; see Record metadata.
Provenance is a uniqueness guard
A _source entry pairs a source (_src) with its record reference
(_src_id). Golden stores the caller’s entries trimmed. A pair is never a
second address: a creation whose pair is already held is refused, and it never
updates the holder.
The guard applies to every creation keyed by a new identifier: a DEFAULT
dataset, and a COLUMN or SCRIPT body with no usable identity. A creation
under a free identity is not checked by pair, because the identity is its key.
Only real pairs are checked — an entry with a non-blank _src_id whose source
is not _golden — so a dataset’s default origin and a source sent without a
reference are never refused.
| Holder of the pair | Answer |
|---|---|
| One or more live records | 409 with existingIds and existingIdsTotal |
| Only records in history | Each holder is followed as an _id in history is: to a live survivor (409 with survivorId), to an undone merge (409 with restoredIds), or to a deletion (410). Of several holders, a live survivor wins over an undo, which wins over a deletion |
Two concurrent first sends of one pair end in one record and one 409.
Writes under a data scope
A caller restricted by a data scope
is never told about a record outside it. Every answer above that would name or
reveal such a record is that record’s 404:
- An update of a live record outside the scope is a
404before any refusal of the update, such as a new_sourceentry, a changed identity or a raw content member. - A cut
existingIdschecks every holder, not only the 20 it names; any holder outside the scope answers the404, soexistingIdsTotalnever reveals one. - A survivor or restored record outside the scope answers as an unknown id.
Delete a record
DELETE /api/golden/{entity}/delete removes one current record. The record
identifier and an optional comment travel in a JSON body:
curl --fail-with-body --silent --show-error \
--request DELETE \
--header "Authorization: Bearer ${GOLDEN_TOKEN}" \
--header "Content-Type: application/json" \
--data '{"recordId":"<record id>","comment":"<reason>"}' \
"${GOLDEN_URL}/api/golden/{entity}/delete"
A successful delete answers deleted: true and the deleted record. The
delete answers history the way the upsert does and never follows a merge to
delete the survivor.
| Situation | Answer |
|---|---|
| A live id, including one sent with surrounding spaces | The record is deleted |
| An id a merge absorbed | 409 with survivorId; the survivor is not deleted |
| The survivor of an undone merge | 409 with restoredIds; 410 when none of them is live |
| A deleted id, including a second delete of the same id | 410 |
| An unknown or blank id, or one outside the caller’s data scope | 404, naming only the id sent |
Tables without history
Golden can answer for an absorbed or deleted record only on a table that keeps history. On a table without history, a merge deletes the records it absorbs, so an upsert or delete of an absorbed or deleted id is treated as unknown, a search for it finds nothing, and reloading an absorbed record’s identity recreates it beside its survivor. Choose history when the table is created for an entity that is reloaded and merged.
Entity synchronization
A synchronization follows the upsert’s rules wherever a bulk load can. Where the upsert answers a refusal, a synchronization counts the row and continues.
| Row | Result |
|---|---|
| An identity column the pipeline canonicalizes | Identity computed from the canonical values: two spellings of one key land on one record |
DEFAULT dataset, a live _id | That record is updated |
DEFAULT dataset, an _id in history (merged, deleted, or an undone survivor) | Left out of the run and counted, as on COLUMN and SCRIPT: a load never resurrects or duplicates a record |
DEFAULT dataset, an _id Golden never issued | Created under a new identifier and counted as created without identity |
| Identity absent or partial | Created under a new identifier and counted as created without identity |
No _id (no column, a blank cell, a JDBC row without its identifier column) on a SCRIPT identity that reads it | Created under a new identifier and counted as created without identity. Its provenance entry names the source without a reference |
| An identity value that cannot key a record | Not written; counted as rejected |
An update whose row carries _source entries | Entries already stored change nothing; new entries are not added, and the row is still written. Golden still records its own source entry |
An update whose row carries _xml, _text or _binary | Those members are ignored; the rest of the row is written |
Re-importing an export of a DEFAULT entity is idempotent.
The run’s completion message reports the rows created without identity and the
rejected rows when either count is not zero, and the entity load’s output
carries them as unresolved and rejected. The run does not list the rejected
rows; check the source’s identity values against
the rules above.
Empty values in a load
On a synchronization a source row is a full snapshot of the columns it carries:
| Source | Emptied on update | Left untouched |
|---|---|---|
| JDBC | A SQL NULL, or an empty array | A column the query does not select |
| CSV | A blank cell | A column the file does not have |
| JSON | "column": null | A member absent from the object |
Table loads
A table load or transform writes only into a table that belongs to no entity.
On such a table, "column": null or a JDBC NULL in an update empties the
member, as for an entity.
| Request | Answer |
|---|---|
POST /api/tables/load whose sinkTable belongs to an entity | 409; nothing is queued. Load the data through the entity’s synchronization |
POST /api/tables/transform over an entity’s table | 409 |
| A run whose target became an entity’s table while it waited | Fails when it starts, before reading anything, with the same message |
| An entity’s table as the source of a load, or exported | Allowed |
The table load’s completion summary lists rows it created without identity and rows it rejected. A table destination follows the same rule: its target table must belong to no entity.
Records stored before Golden 3.1.0
Golden 3.1.0 computes the identity after the entity’s pipeline. Some records stored by an earlier release keep their earlier key:
- A live record created under a generated identifier while its identity column had a value, or stored under a raw spelling its canonical identity no longer equals, is not found by its identity. Its first identity-addressed write creates a record under the identity, once; later writes converge on the new record.
- The survivor of a merge undone before Golden 3.1.0 keeps answering
410. - Rows loaded by an earlier release whose provenance states Golden’s own identifier as the source reference are not rewritten.
Review such records before relying on identity-addressed writes for them.