On this page· 5

Change records

  • file format 6
  • Rust SDK 1.0 preview

Plain words: change notes

Goal: read a log of changes, and know the rules a log follows.

A change record, kind mut, states one change to one record: put this record, or delete that one. A pack that holds only mut records is a change log.

Note

The Rust SDK reads mut records as opaque records. Applications build, check and apply the log themselves, as below.

The members

One change record, as written:

{"k":"mut","v":1,"id":"00000000000000000001","gen":7,"lsn":1,"op":"put_record","rk":"ent","rid":"e1","rec":{"k":"ent","v":1,"id":"e1","t":"Person","name":"Anna","stub":false}}

Table 1. Members of a mut record, version 1: 9
MemberTypeRule
kstringmut
vinteger1
idstringthe sequence number in decimal, padded with zeros to 20 digits. Never an entity id.
genu64the generation: which run of the log the change belongs to
lsnu64the log sequence number: the change's position in its generation
opstringput_record or delete_record; other operation names are reserved for future use
rkstringthe kind of the record being changed; required for put_record and delete_record
ridstringthe primary id of the record being changed; required for put_record and delete_record
recobjectfor put_record only: the whole new record, whose kind and primary id equal rk and rid. delete_record carries no rec.

Members are written in that order, then any others.

The rules a log follows

  • One file holds one generation. Sequence numbers restart when the generation changes.
  • Within a generation, sequence numbers have no gaps, and changes apply in sequence order. The records sort into that order on their own: mut sorts after every typed kind, by id, and a 20-digit padded number sorts as text in number order.
  • There is no partial update. put_record carries the complete record.
  • rk and rid name the changed record; id never does. If id held an entity id, a put and a delete of one record would share a key.
  • A log is ordered, not a set. A producer does not combine two logs with merge.

Apply a log

To apply a log, check each step:

  1. 1. Open the pack with verification on.
  2. 2. Check that every record has kind mut and the members above, and that id equals lsn padded to 20 digits.
  3. 3. Check that every record has the same gen, and that the first lsn follows the last change already applied.
  4. 4. Apply the changes in order: put_record inserts or replaces the record rk, rid with rec; delete_record removes it.
  5. 5. Refuse a record with any other op.

Note

A line with "k":"mut" is not proof of a valid change record. Because the kind is opaque, any JSON object under that name opens. Check the members before acting on one.

The Rust SDK returns mut records as ordinary records. kind() returns mut, and to_json_value() gives the members.

Table 2. A three-change log, in the order a writer emits it: 3 records
lsnoprkrid
1put_recordente1
2delete_recordente2
3put_recordente3

A writer emits records in sort-key order, so the three come out in sequence order whatever order they were given in: all three have kind mut, and their ids are padded numbers that sort as text in number order. Spec §5.3

What happens if two logs are merged

Merge treats a mut record like any record that is not ent: two logs that each hold a change numbered 1 share one dedup key. Under latest the second log's change is kept; under every other strategy, the first's. Either way one change is lost, conflicts stays at 0, and no error is raised. The rule against merging logs is the producer's to keep. Spec §8.1

Reference

Sections