datalevin

Samuel Ludwig 2026-03-31T17:13:33.283749Z

Bit confused, making some inserts via d/transact! on a freshly d/clear'd DB, and noticing that some attributes in certain types of entities (with no clear connection atm) are nil when I get them via d/datoms (note: the attributes themselves are nil, not the values which are populated). Info: • Using version 10.7 • The :tx-data returned looks right, I see the attributes there. • It looks like some of the attributes which are getting nil'd out are the :created-at and :updated-at fields from {:auto-entity-time? true}. • [:*] pulls against these entities throw with bad entity attribute nil: expected keyword. • I wish I could pinpoint exactly when this began, but all I can say is it seemed start 'out of nowhere' yesterday • Debugging efforts included restarting my REPL, using new files for the DB, minimizing my schema Still trying to find a root cause; the only commonality I see between the voided attributes, is that they all seem to be attrs which aren't in my db-schema Minimal reproduction of the error (for my machine at least.. i suspect this might work for someone else for reasons I can't explain):

(def db-schema {:company/name {:db/unique :db.unique/identity}
                :company/linkedin {:db/valueType :db.type/string}})

(def conn
  (d/get-conn "/tmp/datalevin/relay1" db-schema))

(def db (d/db conn))

(d/transact! conn
  [{:company/name "8AM Golf"
    :company/home-page ""
    :company/linkedin ""}])

(d/datoms db :eav 1)
;; => [#datalevin/Datom [1 :company/name "8AM Golf"]
;;     #datalevin/Datom [1 :company/linkedin ""]
;;     #datalevin/Datom [1 nil ""]]

Samuel Ludwig 2026-04-06T14:17:22.162319Z

Can confirm on master the reproduction now gives me all three attributes!

👍 1
Huahai 2026-03-31T17:15:35.318419Z

if you believe there is a bug, please file an GitHub issue

Samuel Ludwig 2026-03-31T17:17:27.165449Z

will do; didn't know if this was a result of a mishandling on my part that was clear to someone more knowledgeable. edit: done https://github.com/datalevin/datalevin/issues/362

Huahai 2026-03-31T19:00:38.694999Z

Thanks!

1
Samuel Ludwig 2026-04-03T15:13:46.675899Z

Let me know if there's anything you'd like me to run on my own (apparently cursed) environment to help debug- I'm still intermittently picking at different functions in the code-base to try and pin-point the breakdown: • I notice (di/fetch store (dd/datom 1 nil nil)) and (di/e-datoms store 1) as seen in db/-search returns the proper attributes when executed alone • Looking at wrap-cache itself, this form returns the `nil' attribute

(.get 
  (.get (deref #'ddb/caches) (di/dir store)) 
  [:search 1 nil nil])
• If I (ddb/refresh-cache store), and then (d/touch entity), I still get the voided attribute (honestly, i thought this one would work~)

Huahai 2026-04-03T19:12:13.505779Z

These data points are very helpful. Will push a fix soon.

1
🙏 1
Huahai 2026-04-03T20:46:53.816949Z

master branch has a fix. please test if it works for you.

Huahai 2026-04-02T00:01:20.821249Z

Unable to reproduce in the 0.10.7 release, nor in current master branch

Samuel Ludwig 2026-04-02T14:02:56.179309Z

I can't say I'm surprised (the error seems so fundamental that I'd assume it'd be difficult to replicate, otherwise everyone would be sounding alarms on it). I have been bypassing it for now by putting every attribute in my db-schema, but it certainly is still happening- persisting through even machine restarts...super-odd. I'll instrument dtlv with flowstorm and see if anything jumps out at me edit: flowstorm starts to blow up fast with dtlv's background loop- that is going to be much harder to keep track of~

Samuel Ludwig 2026-04-02T15:31:09.701509Z

Hmm, it looks like the attribute might still be in the DB itself?

#datalevin.db.DB{
 :store #object[datalevin.storage.Store 0x4f8e7564 "datalevin.storage.Store@4f8e7564"], 
 :max-eid 0, :max-tx 1, 
 :eavt #{#datalevin/Datom [1 :company/home-page ""] #datalevin/Datom [1 :company/linkedin ""] #datalevin/Datom [1 :company/name "8AM Golf"]}, 
 :avet #{#datalevin/Datom [1 :company/home-page ""] #datalevin/Datom [1 :company/linkedin ""] #datalevin/Datom [1 :company/name "8AM Golf"]}, 
 :pull-patterns #object[datalevin.utl.LRUCache 0x70ab6a70 "datalevin.utl.LRUCache@70ab6a70"]}
But the subsequent call to (-datom :eav 1) still has our nil; so maybe its an issue with retrieving values rather than how their stored? (unless the root issue is indexing related, i guess)

Samuel Ludwig 2026-04-02T15:38:51.222559Z

confirming that :wal? false does not affect the error

Samuel Ludwig 2026-04-02T16:12:12.538939Z

sadly i can't easily introspect what's happening in the wrap-cache/`slice` macros (which is what actually returns the nil attribute)

Huahai 2026-04-02T16:21:26.928339Z

looks to me a schema issue, can you show the schema?

Samuel Ludwig 2026-04-02T16:22:22.769839Z

just this, from above

(def db-schema {:company/name {:db/unique :db.unique/identity}
                :company/linkedin {:db/valueType :db.type/string}})

Huahai 2026-04-02T16:39:24.296729Z

so home-page is not there, that's the bug

Huahai 2026-04-02T16:40:16.822259Z

Datalevin is schema on write, apparently, this is somehow broken in your case.

Huahai 2026-04-02T16:43:11.233919Z

Would need to have a repro to find the conditions for this to happen

Samuel Ludwig 2026-04-02T16:43:58.245449Z

ah im very sorry i misunderstood your question, i thought you were asking what i supplied as the initial schema

Samuel Ludwig 2026-04-02T16:44:26.551309Z

heres the output of d/schema

;; => {:company/linkedin #:db{:valueType :db.type/string, :aid 4},
;;     :company/name #:db{:unique :db.unique/identity, :aid 3},
;;     :db/created-at
;;     #:db{:valueType :db.type/long,
;;          :cardinality :db.cardinality/one,
;;          :aid 1},
;;     :db/ident
;;     #:db{:unique :db.unique/identity,
;;          :valueType :db.type/keyword,
;;          :aid 0},
;;     :db/updated-at
;;     #:db{:valueType :db.type/long,
;;          :cardinality :db.cardinality/one,
;;          :aid 2},
;;     :company/home-page #:db{:aid 5}}

Huahai 2026-04-02T16:45:09.989509Z

oh, so home-page is there.

Huahai 2026-04-02T16:45:38.848839Z

did you upgraded the db from prior version?

Samuel Ludwig 2026-04-02T16:47:06.573829Z

it should be as fresh as it comes, the path is a directory i made today

Huahai 2026-04-02T16:48:51.451249Z

if this is fresh, then I don't know why it is not reproducible

Samuel Ludwig 2026-04-02T16:50:09.479679Z

definitely understand, im sure there's some ghost somewhere in my machine

Samuel Ludwig 2026-04-02T17:09:22.581209Z

any reason why we avoid running vld/validate-attr (which would throw an exception) in resolve-datom when the attribute is nil?

(defn- resolve-datom
  [db e a v default-e default-v]
  (when a (vld/validate-attr a (list 'resolve-datom 'db e a v default-e default-v)))
  (let [v? (some? v)]
    (datom
      (or (entid-some db e) default-e)  ;; e
      a                                 ;; a
      (if (and v? (ref? db a))          ;; v
        (entid-strict db v)
        (if v? v default-v)))))

Huahai 2026-04-02T17:28:08.805299Z

because resolve-datom is for building patterns, it's possible a is nil for patterns, as nil means wildcard in patterns. We don't want to validate a nil attribute.

Huahai 2026-04-02T17:28:48.123799Z

Otherwise, e _ _ pattern would blow up.

Huahai 2026-04-02T17:39:43.233999Z

These patterns are used as index boundary (range query)

Samuel Ludwig 2026-04-02T20:29:01.691419Z

ahhh I see I see, hmm. Noticing the call below to slice alone (outside of wrap-cache) does seem to return the tuples I expect

(di/slice
    (->> @conn :store)
    :eav
    (#'ddb/components->pattern db :eav 1 nil nil 0 nil)
    (#'ddb/components->pattern db :eav 1 nil nil 0x7FFFFFFFFFFFFFFF nil))
  
;; => [#datalevin/Datom [1 :company/name "8AM Golf"]
;;     #datalevin/Datom [1 :company/linkedin "https: //www.linkedin.com/company/8am-golf/"] ; uri's edited to avoid slack embeds, not an error
;;     #datalevin/Datom [1 :company/home-page "https: //8amgolf.com/"]]

Samuel Ludwig 2026-04-02T20:39:11.710809Z

trying to call it with wrap-cache but REPL is complaining about caches being private

Samuel Ludwig 2026-04-02T21:17:08.178999Z

Ok, so it's wrap-cache which seems to be causing the voided attribute

(alter-meta! #'ddb/caches assoc :private false)
(ddb/wrap-cache
  (->> @conn :store)
  [:datoms :eav 1 nil nil]
  (di/slice
    (->> @conn :store)
    :eav
    (#'ddb/components->pattern db :eav 1 nil nil 0 nil)
    (#'ddb/components->pattern db :eav 1 nil nil 0x7FFFFFFFFFFFFFFF nil)))

;; => [#datalevin/Datom [1 :company/name "8AM Golf"]
;;     #datalevin/Datom [1 :company/linkedin ""]
;;     #datalevin/Datom [1 nil ""]]

Huahai 2026-04-02T21:18:13.413779Z

👍 will take a look

Samuel Ludwig 2026-04-02T21:52:22.622969Z

another data-point: (d/touch (d/entity db 1)) shows that attribute nil'd out as well, so doubling down on 'maybe cache-related'; if the lone slice call didn't return the correct tuple I'd think the attribute really was gone in the DB

Samuel Ludwig 2026-04-02T22:21:59.932979Z

ah, the datalevin.db/-search in touch is what returns the nil attribute (edit: which I see also uses wrap-cache, which makes sense~)