Skip to content

GH-163: Stage only the difference from a Record's last synchronized state on save - #164

Open
jtnelson wants to merge 1 commit into
developfrom
feature/GH-163
Open

GH-163: Stage only the difference from a Record's last synchronized state on save#164
jtnelson wants to merge 1 commit into
developfrom
feature/GH-163

Conversation

@jtnelson

@jtnelson jtnelson commented Aug 6, 2026

Copy link
Copy Markdown
Member

Summary

A save previously wrote a Record's entire remembered state, so an instance with a stale view erased changes that other writers committed after the instance was loaded (the seat and vault data loss in cinchapi/cinchapi-server#151 and cinchapi/cinchapi-server#145). A save now writes only the difference between the record's current state and the state it last loaded or saved.

Fixes #163.

Behavior

  • A collection field and realm membership stage per-element adds and removes. Elements and realms that other writers added or removed concurrently survive the save, including when the instance empties the collection.
  • A scalar field is written only when its value changed, and cleared only when a previously present value was removed. A field the instance never touched stages nothing, so a concurrent change to it survives. When two writers change the same scalar field, the later save still wins for that field.
  • hasUnsavedChanges() reports whether a save would write anything. A mutation with no serialized effect (for example, reordering a List, since the database stores an unordered set of values) is not an unsaved change.
  • A save is a statement of the changes the instance made, not of complete record state: re-saving a record does not repair stored data that drifted through writes made outside of Runway, and assigning a collection wholesale does not guarantee that storage exactly matches the assigned collection when other writers changed the stored set concurrently.
  • Saver gained add and no longer has reconcile.
  • Failed-save restore, beforeSave gating, save and delete listeners, preventStaleWrites, and the single-key atomic operations keep their existing contracts.

Scope

  • Regression coverage is in GH163; its reproduction tests fail against the prior save semantics.
  • The changelog entry sits under Version 3.0.0 (unreleased). The .version bump is deferred to release.

…tate on save

A save previously wrote a Record's entire remembered state, so an
instance with a stale view erased changes that other writers committed
after it was loaded. Every Record now keeps a per-field baseline of the
state it last loaded or persisted, and a save stages only the
difference: per-element adds and removes for collection fields and
realm membership, and a write or clear for a scalar field only when its
value changed. A field the instance never touched stages nothing, so
concurrent changes to it survive.

hasUnsavedChanges() now reports whether a save would write anything, so
a mutation with no serialized effect (e.g., reordering a List) is no
longer an unsaved change. The baseline replaces the whole-record
checksum and the realm modification flag as the single change-tracking
mechanism. Saver gained add and no longer has reconcile.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Saves write the full remembered state of a Record, erasing concurrent changes to untouched fields

1 participant