tonsky / datascript

Immutable database and Datalog query engine for Clojure, ClojureScript and JS
Eclipse Public License 1.0
5.45k stars 304 forks source link

Inaccurate temporary IDs after transit deserialization #463

Closed samcf closed 4 months ago

samcf commented 5 months ago

Ran into an issue when I was mistakenly using empty maps as values for attributes with a :db/valueType of :db.type/ref. When the database is serialized and deserialized using datascript-transit, creating a new entity with a temporary ID of -1 ends up resolving to an ID that already "exists" in the database.

I'm not sure which symptom is more foundational: allowing a reference to an entity with no attributes or that the temporary ID resolved to an ID which was being used as a reference.

I'm also not sure that deserialization is necessary to reproduce, but I couldn't find another way to do it: all other methods resolve -1 to the expected new unique ID.

Code to reproduce

(let [;; create a new database.
      init (ds/empty-db {:mother {:db/valueType :db.type/ref}})
      data (ds/db-with init [[:db/add 1 :name "Sam"]])
      conn (ds/conn-from-db data)

      ;; this creates a new entity and relates it to 1's :mother.
      ;; i don't think this part is a bug, but using empty maps
      ;; to implicitly create new entities is not very useful,
      ;; particularly because you can't have entities with no
      ;; attributes afaik.
      _    (ds/transact! conn [{:db/id 1 :mother {}}])

      ;; this is at least one way to run into this issue:
      ;; serializing and deserializing the database causes the newly
      ;; added entity to re-use an entity id, despite using temp -1.
      seri (dt/write-transit-str @conn)
      conn (ds/conn-from-db (dt/read-transit-str seri))
      _    (ds/transact! conn [{:db/id -1 :name "John"}])]

  ;; inspecting the datoms we can see that the entity "John"
  ;; is using the same :db/id as our empty map entity. "John"
  ;; shouldn't be my mother's name! Expected "John" to have
  ;; a :db/id of 3, not 2.
  (prn (ds/datoms @conn :eavt)))