Add mechanism for data migrations

This commit is contained in:
arne 2025-11-22 09:04:06 +01:00
commit 54c57e085a
4 changed files with 137 additions and 32 deletions

View file

@ -4,6 +4,7 @@
[clojure.string :as str]
[clojure.pprint :as pprint]
[computersandblues.lodestone.database :as db]
[computersandblues.lodestone.interop :refer [promise-all promise-resolve promise-reject]]
[computersandblues.lodestone.match :refer [query->matching-xform]]
[applied-science.js-interop :as j]))
@ -34,15 +35,6 @@
;; Mastodon API helpers
(defn- promise-all [xs]
(js/Promise.all (apply array xs)))
(defn- promise-resolve [val]
(js/Promise.resolve val))
(defn- promise-reject [val]
(js/Promise.reject val))
(defn- now []
(js/Date.now))
@ -560,26 +552,45 @@
;; database
(def db-version 3)
;;; we provide two different kinds of migrations
;;;
;;; - structural migrations
;;; - data migrations
;;;
;;; this is necessary due to the constraints imposed on us by indexeddb.
;;;
;;; structural migrations run in a callback after opening the database. they run
;;; synchronously and do things like creates stores and indices. because they run
;;; synchronously, they cannot modify any of the stored data.
;;;
;;; data migrations can do that! they run after all structural migrations for
;;; the current database version have succeeded, and work on the open database.
;;;
;;; note that for each db-version, there needs to be at least one structural or
;;; data migration. it is fine to have both for a single version, in which case
;;; the structural migration will run first.
(def migrations
{1 (fn migration-0001 [db _]
(def db-version 4)
(def structural-migrations
{1 (fn =0001-create-stores [db _]
(db/create-object-store! db ::db/application {:keyPath "id"})
(db/create-object-store! db ::db/posts {:keyPath "id"}))
2 (fn migration-0002 [_ txn]
2 (fn =0002-add-post-created-at-idx [_ txn]
(-> (db/open-store txn ::db/posts "readwrite")
(db/create-index! ::db/post-created-at "created_at" {:unique false})))
3 (fn migration-0003 [_ txn]
3 (fn =0003-add-post-internal-id-idx [_ txn]
(-> (db/open-store txn ::db/posts "readwrite")
(db/create-index! ::db/post-internal-id "internal_id" {:unique false})))})
(defn- convert-internal-ids! []
; TODO figure out when we can remove this again
(let [cursor (db/open-cursor ::db/posts ::db/all)
result (db/transduce-cursor (filter (comp string? (j/get :internal_id))) cursor)]
(.then result (fn [rows]
(doseq [row rows]
(db/put! ::db/posts (j/update! row :internal_id parse-long)))))))
(def data-migrations
{4 (fn =0004-convert-internal-ids [db]
(let [cursor (db/open-cursor db ::db/posts ::db/post-internal-id ::db/all)
result (db/transduce-cursor (filter (comp string? (j/get :internal_id))) cursor)]
(.then result
(fn [rows]
(promise-all (for [row rows]
(db/put! ::db/posts (j/update! row :internal_id parse-long))))))))})
;; go go go
@ -589,8 +600,7 @@
(defn init []
(swap! state assoc :root (rd/create-root (js/document.querySelector "#root")))
(render)
(-> (db/setup! ::database db-version migrations)
(-> (db/setup! ::database db-version structural-migrations data-migrations)
(.then #(setup-application!))
(.then #(convert-internal-ids!))
(.catch (fn [err]
(js/console.error ::db/setup! err)))))