Skip to content

native: save() of a saved record merges every property, not the ones that changed #245

Open

Description

@tae898

save() of a saved record on the native build runs UPDATE :rid MERGE :row with row = every property the object holds, whether or not it changed. Since #194 the stored row is compared with the copy of the values as read before the statement runs, so every value that is as read is already stored: sending it again costs parameter encoding, a larger crossing payload and an engine merge of values that do not change. For a record with a vector (768 floats) and 20 other properties, one changed property sends the whole vector each time.

Proposal (Python only, src/arcadedb/_native/records.py): send only the values that changed, as the caller holds them (an ndarray stays an array). A save that changes nothing still runs its UPDATE with every value, so update events fire as before.

What this does not change: the insert path. save() of a new record adopts the stored row from the statement's reply (the engine converts values to the declared property types, which the record object does not know), so that reply is needed; the update path already skips its read-back for values stored as sent (#194), and command rows are already pulled but not encoded until read (arcadedb_n_cmd). A cheaper insert reply needs a change in the native library (an image rebuild), which is out of scope here.

Measurement: a step-0 probe (untimed) and then a timed A/B on the #194 workload, written to the repo's study standard before any run. The PR is stacked on #194 (the save path it changes) and stays draft until the study is read.

Activity

  1. tae898 commented on Nov 4, 2026

    @tae898
    CollaboratorAuthor

    Timed A/B of PR #246 on the native build (laptop timed lane, tools/ab.py --noise, ABBA order, round 0 dropped, an A/A control). Arm A = main 7e64c5e plus PR #194 ea2b429, arm B = PR #246 324e41d. Per-save time, B/A median of the per-round ratios with its 95% interval (below 1 = PR #246 faster):

    workload role B/A median [95% CI]
    small record (vertex P, id and n), one property changed gating 1.002 [0.991, 1.007]
    medium record (20 properties and a 768-float vector), one property changed gating 0.797 [0.792, 0.802]
    first save of a new small record control 1.014 [0.995, 1.017]
    first save of a new medium record control 0.995 [0.990, 1.002]
    unchanged first touch control 1.011 [1.003, 1.013]

    Clean pairs per figure: 11; every A/A median between 0.998 and 1.002.

    By the rule written before the run, PR #246 ships only if both gating figures are faster. The medium record saves faster (0.797 [0.792, 0.802]), while the small record's interval lies inside [0.97, 1.03] (1.002 [0.991, 1.007]), so the verdict is GRAY. Speed was the only reason for the change and the rule makes a GRAY final, so save() keeps the behaviour of #194 and PR #246 is closed. The medium-record result is recorded here in case a later change targets saves of large records.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions