diff --git a/backend/src/app/binfile/common.clj b/backend/src/app/binfile/common.clj index beaae575b8..7f8df26fda 100644 --- a/backend/src/app/binfile/common.clj +++ b/backend/src/app/binfile/common.clj @@ -332,11 +332,18 @@ ;; same decode `fsnap/get-snapshot` applies: storage-backed ;; snapshots carry the storage ref inside :metadata, so it must - ;; be decoded before `resolve-file-data` can fetch the object + ;; be decoded before `resolve-file-data` can fetch the object. + ;; NOTE the (cfg file) argument order of both fdata fns: the + ;; snapshot row is the FILE argument, never the cfg. base (-> base - (d/update-when :metadata fdata/decode-metadata) - (fdata/resolve-file-data cfg) - (fdata/decode-file-data cfg)) + (d/update-when :metadata fdata/decode-metadata)) + + base (if (nil? (:migrations base)) + (dissoc base :migrations) + (update base :migrations db/pgarray->set)) + + base (fdata/resolve-file-data cfg base) + base (fdata/decode-file-data cfg base) rows (db/exec! conn [sql:get-branch-changes id]) @@ -346,9 +353,16 @@ rows) data (if decode? data (blob/encode data))] - (assoc file :data data))) - - + (-> file + ;; the derived data is the BASE's data plus the op log, so the + ;; file carries the base's version and migration set; this keeps + ;; `need-migration?` quiet while base and main share a version + ;; and migrates in memory (read-only) only when the base is + ;; actually stale + (assoc :data data) + (assoc :version (or (:version base) (:version file))) + (cond-> (some? (:migrations base)) + (assoc :migrations (:migrations base)))))) (defn- get-file* [{:keys [::db/conn] :as cfg} id {:keys [migrate? diff --git a/backend/src/app/rpc/commands/files_branch.clj b/backend/src/app/rpc/commands/files_branch.clj index 7de3ff2c8a..bb41ea708e 100644 --- a/backend/src/app/rpc/commands/files_branch.clj +++ b/backend/src/app/rpc/commands/files_branch.clj @@ -940,7 +940,7 @@ ;; into replayable ops; nothing has been ;; persisted yet, so the transaction rollback ;; undoes the snapshot and media copies above. - (let [{:keys [squashed] + (let [{net-changes :changes unsupported :unsupported} (bm/compute-changes main-data main-data (:data updated) {})] (when (seq unsupported) @@ -948,7 +948,7 @@ :code :unsupported-update-squash :hint "the update produces branch-only changes that cannot be replayed" :kinds (vec unsupported))) - (persist-branch-update! cfg updated ts branch-id squashed)) + (persist-branch-update! cfg updated ts branch-id net-changes)) (reposition-base! ts (:revn updated)) (mbus/pub! msgbus diff --git a/backend/test/backend_tests/rpc_file_branch_oplog_test.clj b/backend/test/backend_tests/rpc_file_branch_oplog_test.clj index 1182b08f3b..39c4f0b226 100644 --- a/backend/test/backend_tests/rpc_file_branch_oplog_test.clj +++ b/backend/test/backend_tests/rpc_file_branch_oplog_test.clj @@ -46,8 +46,11 @@ (defn- oplog-rows [branch-file-id] (th/db-query :file-branch-change {:file-id branch-file-id})) +;; only "main" rows: the auto-file-snapshot machinery appends "snapshot" +;; rows on every save, which are checkpoints, not a stored data payload (defn- stored-data-rows [file-id] - (th/db-query :file-data {:file-id file-id})) + (->> (th/db-query :file-data {:file-id file-id}) + (filterv #(= "main" (:type %))))) (t/deftest branch-stores-no-data-and-derives-state (with-redefs [cf/flags (conj cf/flags :branching)] @@ -144,7 +147,7 @@ (t/testing "the op log is replaced by the branch-only net" (let [rows (oplog-rows branch-file-id) - ops (mapv (comp blob/decode :changes) rows)] + ops (-> rows first :changes blob/decode)] (t/is (= 1 (count rows))) (t/is (some #(and (= :add-color (:type %)) (= branch-color (get-in % [:color :id])))