diff --git a/backend/src/app/graph/ingest.clj b/backend/src/app/graph/ingest.clj index 8b7d29d81b..5623f09431 100644 --- a/backend/src/app/graph/ingest.clj +++ b/backend/src/app/graph/ingest.clj @@ -40,12 +40,14 @@ {:keys [statements stats]} (project.document/projection-statements data file) ingest-statements (conj (into ddl statements) "CHECKPOINT;")] - (ladybug/exec! db-path ingest-statements) - {:file-id file-id - :revn (:revn file) - :name (or (:name data) (:name file)) - :db-path db-path - :schema-version schema/schema-version - :projection {:stats stats} - :transforms (project.transforms/apply-transforms! system db-path data file) - :stats (stats/summarize db-path)}))) + (ladybug/with-connection! db-path + (fn [conn] + (ladybug/exec-on-connection! conn ingest-statements) + {:file-id file-id + :revn (:revn file) + :name (or (:name data) (:name file)) + :db-path db-path + :schema-version schema/schema-version + :projection {:stats stats} + :transforms (project.transforms/apply-transforms! system db-path data file) + :stats (stats/summarize-connection conn)}))))) diff --git a/backend/src/app/graph/ladybug.clj b/backend/src/app/graph/ladybug.clj index f984b6bba6..9c8998fcb2 100644 --- a/backend/src/app/graph/ladybug.clj +++ b/backend/src/app/graph/ladybug.clj @@ -91,19 +91,7 @@ :statement statement :err (.getErrorMessage result)))) -(defn- with-connection - [db-path f] - (let [^Database db (if (memory-db-path? db-path) - (Database.) - (Database. (str db-path)))] - (try - (let [^Connection conn (Connection. db)] - (try - (f conn) - (finally - (.close conn)))) - (finally - (.close db))))) +(def ^:private default-query-timeout-seconds 120) (defn- scalar-value [^Connection conn statement] @@ -122,36 +110,64 @@ (with-open [^QueryResult result (.query conn cypher)] (check-success! result cypher))))) -(def ^:private default-query-timeout-seconds 120) +(defn- ensure-db-path! + [db-path] + (when-not (memory-db-path? db-path) + (fs/create-dir (fs/parent db-path)))) + +(defn with-connection! + "Open a Ladybug connection for `db-path` and invoke `(f conn)`. + + For `:memory:`, the database only lives for the duration of this call; + all reads and writes must happen inside `f`." + [db-path f] + (ensure-db-path! db-path) + (let [^Database db (if (memory-db-path? db-path) + (Database.) + (Database. (str db-path)))] + (try + (let [^Connection conn (Connection. db)] + (try + (.setQueryTimeout conn default-query-timeout-seconds) + (f conn) + (finally + (.close conn)))) + (finally + (.close db))))) + +(defn exec-on-connection! + "Execute Cypher statements on an open Ladybug connection." + [^Connection conn statements] + (assert (sequential? statements) "statements should be a sequential collection") + (run-statements! conn statements)) + +(defn query-scalar-on-connection! + "Execute a query expected to return a single scalar value on `conn`." + [^Connection conn statement] + (scalar-value conn statement)) (defn exec! "Execute Cypher statements against a Ladybug database. `db-path` is either `:memory:` or a filesystem path to a `.lbug` database." [db-path statements] - (assert (sequential? statements) "statements should be a sequential collection") - (when-not (memory-db-path? db-path) - (fs/create-dir (fs/parent db-path))) - (with-connection db-path - (fn [^Connection conn] - (.setQueryTimeout conn default-query-timeout-seconds) - (run-statements! conn statements)))) + (with-connection! db-path + (fn [conn] + (exec-on-connection! conn statements)))) (defn query-scalar! "Execute a query expected to return a single scalar value." [db-path statement] - (with-connection db-path - (fn [^Connection conn] - (.setQueryTimeout conn default-query-timeout-seconds) - (scalar-value conn statement)))) + (with-connection! db-path + (fn [conn] + (query-scalar-on-connection! conn statement)))) (defn smoke-test! "Run a minimal CREATE + count against Ladybug." [& {:keys [db-path] :or {db-path ":memory:"}}] (when-not (memory-db-path? db-path) - (reset-db-path! db-path) - (fs/create-dir (fs/parent db-path))) - (with-connection db-path + (reset-db-path! db-path)) + (with-connection! db-path (fn [^Connection conn] (run-statements! conn ["CREATE NODE TABLE Person(name STRING, age INT64, PRIMARY KEY(name));" diff --git a/backend/src/app/graph/stats.clj b/backend/src/app/graph/stats.clj index 282f982e0d..dee05ce2f3 100644 --- a/backend/src/app/graph/stats.clj +++ b/backend/src/app/graph/stats.clj @@ -15,20 +15,25 @@ (str "`" table "`") table)) -(defn- count-query - [db-path statement] - (or (ladybug/query-scalar! db-path statement) 0)) +(defn- count-on-connection + [conn statement] + (or (ladybug/query-scalar-on-connection! conn statement) 0)) + +(defn summarize-connection + "Return node/edge counts using an open Ladybug connection." + [conn] + {:nodes (into {} + (map (fn [{:keys [name]}] + [name (count-on-connection + conn + (str "MATCH (n:" (node-label-for-match name) ") " + "RETURN count(n) AS " name "_c;"))]) + schema/node-tables)) + :edges {:IsChildOf (count-on-connection + conn + "MATCH ()-[e:IsChildOf]->() RETURN count(e) AS IsChildOf_c;")}}) (defn summarize "Return node/edge counts from the graph database." [db-path] - {:nodes (into {} - (map (fn [{:keys [name]}] - [name (count-query - db-path - (str "MATCH (n:" (node-label-for-match name) ") " - "RETURN count(n) AS " name "_c;"))]) - schema/node-tables)) - :edges {:IsChildOf (count-query - db-path - "MATCH ()-[e:IsChildOf]->() RETURN count(e) AS IsChildOf_c;")}}) + (ladybug/with-connection! db-path summarize-connection)) diff --git a/backend/src/app/srepl/main.clj b/backend/src/app/srepl/main.clj index a30663a21a..c1f2e312f1 100644 --- a/backend/src/app/srepl/main.clj +++ b/backend/src/app/srepl/main.clj @@ -431,7 +431,7 @@ - `:db-path` path or `:memory:` - `:reset-db?` delete any existing db first (default true)" [file-id & opts] - (let [result (graph.ingest/ingest-file! main/system file-id opts)] + (let [result (apply graph.ingest/ingest-file! main/system file-id opts)] (graph.report/print-ingest! result) result))