;; This Source Code Form is subject to the terms of the Mozilla Public ;; License, v. 2.0. If a copy of the MPL was not distributed with this ;; file, You can obtain one at http://mozilla.org/MPL/2.0/. ;; ;; Copyright (c) KALEIDOS INC ;; This is an example on how it can be executed: ;; clojure -Scp $(cat classpath) -M dev/script-fix-sobjects.clj (require '[app.common.logging :as l] '[app.common.data :as d] '[app.common.pprint] '[app.db :as db] '[app.storage :as sto] '[app.storage.impl :as impl] '[app.util.time :as dt] '[integrant.core :as ig]) ;; --- HELPERS (l/info :hint "initializing script" :args *command-line-args*) (def noop? (some #(= % "noop") *command-line-args*)) (def chunk-size 10) (def sql:retrieve-sobjects-chunk "SELECT * FROM storage_object WHERE created_at < ? AND deleted_at is NULL ORDER BY created_at desc LIMIT ?") (defn get-chunk [conn cursor] (let [rows (db/exec! conn [sql:retrieve-sobjects-chunk cursor chunk-size])] [(some->> rows peek :created-at) (seq rows)])) (defn get-candidates [conn] (->> (d/iteration (partial get-chunk conn) :vf second :kf first :initk (dt/now)) (sequence cat))) (def modules [:app.db/pool :app.storage/storage [:app.main/default :app.worker/executor] [:app.main/assets :app.storage.s3/backend] [:app.main/assets :app.storage.fs/backend]]) (def system (let [config (select-keys app.main/system-config modules) config (-> config (assoc :app.migrations/all {}) (assoc :app.metrics/metrics nil))] (ig/load-namespaces config) (-> config ig/prep ig/init))) (defn update-fn [{:keys [conn] :as storage} {:keys [id backend] :as row}] (cond (= backend "s3") (do (l/info :hint "rename storage object backend" :id id :from-backend backend :to-backend :assets-s3) (assoc row :backend "assets-s3")) (= backend "assets-s3") (do (l/info :hint "ignoring storage object" :id id :backend backend) nil) (or (= backend "fs") (= backend "assets-fs")) (let [sobj (sto/row->storage-object row) path (-> (sto/get-object-path storage sobj) deref)] (l/info :hint "change storage object backend" :id id :from-backend backend :to-backend :assets-s3) (when-not noop? (-> (impl/resolve-backend storage :assets-s3) (impl/put-object sobj (sto/content path)) (deref))) (assoc row :backend "assets-s3")) :else (throw (IllegalArgumentException. "unexpected backend found")))) (try (db/with-atomic [conn (:app.db/pool system)] (let [storage (:app.storage/storage system) storage (assoc storage :conn conn)] (loop [items (get-candidates conn)] (when-let [item (first items)] (when-let [{:keys [id] :as row} (update-fn storage item)] (db/update! conn :storage-object (dissoc row :id) {:id (:id item)})) (recur (rest items)))) (when noop? (throw (ex-info "explicit rollback" {}))))) (catch Throwable cause (cond (= "explicit rollback" (ex-message cause)) (l/warn :hint "transaction aborted") :else (l/error :hint "unexpected exception" :cause cause)))) (ig/halt! system) (System/exit 0)