Possible to create a new entity using aliased ident of another entity

Hi, I used the alias feature like it is described here https://docs.datomic.com/pro/best-practices.html#use-aliases all works as expected but what was not expected was that it was possible to create a new entity using the initial ident which already had an alias ident.
So interesting case happens: on the one hand, you can get an entity by original ident and by alias ident on the other hand you can create a new entity using an original ident which before creating the new entity refers to another existing entity.
Is it expected behavior?

Steps to reproduce the issue:

(require '[datomic.api :as d])
(d/create-database "datomic:mem://test10")
(def conn (d/connect "datomic:mem://test10"))


(def post-schema [
                   {
                       :db/ident :Post/title.version.1
                       :db/valueType :db.type/ref
                       :db/cardinality :db.cardinality/one
                   }
                ])
(d/transact conn post-schema)

(def entityByAlias1_1 (d/entity (d/db conn) :Post/title1))
(def entityByOriginalIdent_1 (d/entity (d/db conn) :Post/title.version.1))

 (d/transact
      conn
[[:db/add :Post/title.version.1 :db/ident :Post/title1]])

;; The next two calls return the same entity
(def entityByAlias1_1 (d/entity (d/db conn) :Post/title1))
(def entityByOriginalIdent_1 (d/entity (d/db conn) :Post/title.version.1))


(def post-schema [
                   {
                       :db/ident :Post/title.version.1
                       :db/valueType :db.type/keyword
                       :db/cardinality :db.cardinality/one
                   }
                ])
; I would expect the transaction should fail here
(d/transact conn post-schema)

;; The next two calls return two different entity
(def entityByAlias1_1 (d/entity (d/db conn) :Post/title1))
(def entityByOriginalIdent_1 (d/entity (d/db conn) :Post/title.version.1))


Without access to the codebase of datomic, I think that :db/ident could be thought of like an index that evolves like this in your example:

after first transaction of post-schema:

{:Post/title.version.1 <initial-schema-entity>}

After second transaction, the one that changes the ident, there is still a link to the same entity from the previous :db/ident key, like so:

{:Post/title.version.1 <initial-schema-entity> ;; "silent survivor"
 :Post/title1 <initial-schema-entity>}

After the third transaction, the ident :Post/title.version.1 is pointing to the new schema entity:

{:Post/title.version.1 <SECOND-schema-entity>
 :Post/title1 <initial-schema-entity>}

The reason that the old ident “silently survives” seems to be a pragmatic choice to make backward compatibility in the code working against the database. It could certainly be regarded as “less correct” to some extent, but it makes the running code work unchanged (as long as the :db/ident is not assigned to a new entity) which makes redeploys of schema and system code possible to do in several steps.

Actually this double-reference by idents already occurs in some of the built in functions, that works when called both as :db.fn/X and :db/X. I cannot test which one right now, it might be retractEntity.

The :db/ident is still a :db.cardinality/one attribute, so after the lookup in the ident index (which according to Datomic documentation is kept in peer), the listing of “silent survivor” key can only be found looking into the transaction history, but can still be used to reference entities. The documentation also explicitly mentions that renaming attributes to a new name is ok, but avoid to re-use attribute names.

1 Like