Last active
August 1, 2024 11:09
-
-
Save qnkhuat/23552c5defc9db007de83f463a85e681 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns stress-big-dashboard | |
(:require | |
[clojure.java.shell :as sh] | |
[metabase.sync :as sync] | |
[metabase.test :as mt] | |
[toucan2.core :as t2] | |
[toucan2.tools.with-temp :as t2.with-temp])) | |
""" | |
## | |
- AppDBs: Postgres and MySQL | |
- Data Warehouses: Postgres, BigQuery, Snowflake | |
- Dashboard with many dashcards based on models and filters | |
- 100 tables | |
- with 100 columns each, of many types | |
- and with 100 rows of random data each | |
- 100 MBQL models, one for each table, that returns the whole thing | |
- 100 MBQL queries, one for each model, that does `COUNT` | |
- 1 dashboard | |
- with 19 filters (9 int category fields, 10 string category fields) | |
- with all 100 `COUNT` queries in one tab | |
- all wired to all 19 filters | |
- Sandboxing: users who are sandboxed and users who are not | |
""" | |
#_(def create-db-sql | |
" | |
DO | |
$$ | |
DECLARE | |
table_idx INT; | |
column_idx INT; | |
col_type_idx INT; | |
col_types TEXT[] := ARRAY['INT', 'TIMESTAMP', 'FLOAT', 'TEXT']; | |
col_type TEXT; | |
insert_stmt TEXT; | |
BEGIN | |
FOR table_idx IN 1..100 LOOP | |
EXECUTE format('CREATE TABLE table_%s (', table_idx) || | |
array_to_string(ARRAY( | |
SELECT format('col_%s_%s %s', table_idx, col_idx, col_types[(col_idx % 4) + 1]) | |
FROM generate_series(1, 100) AS col_idx | |
), ', ') || | |
');'; | |
insert_stmt := ''; | |
FOR column_idx IN 1..100 LOOP | |
col_type_idx := (column_idx % 4) + 1; | |
col_type := col_types[col_type_idx]; | |
IF col_type = 'INT' THEN | |
insert_stmt := insert_stmt || format('%s', floor(random())::int) || ', '; | |
ELSIF col_type = 'TIMESTAMP' THEN | |
insert_stmt := insert_stmt || 'current_timestamp, '; | |
ELSIF col_type = 'FLOAT' THEN | |
insert_stmt := insert_stmt || 'random(), '; | |
ELSE | |
insert_stmt := insert_stmt || format('''sample_text_%s''', column_idx) || ', '; | |
END IF; | |
END LOOP; | |
insert_stmt := left(insert_stmt, -2); -- Remove last comma and space | |
FOR row_idx IN 1..100 LOOP | |
EXECUTE format('INSERT INTO table_%s VALUES (%s);', table_idx, insert_stmt); | |
END LOOP; | |
END LOOP; | |
END | |
$$; | |
") | |
(defn create-db! | |
[db-name] | |
(let [db (t2/insert-returning-instance! | |
:model/Database | |
(merge | |
(t2.with-temp/with-temp-defaults :model/Database) | |
{:name db-name | |
:engine :postgres | |
:details {:host "localhost" | |
:port 5432 | |
:dbname db-name | |
:user "postgres"}}))] | |
(sync/sync-database! db) | |
db)) | |
(defn create-cards | |
"Given a db, create a model for each table of the DB. | |
Then for each model, create a question based on that model with :count aggregation." | |
[db-id] | |
(let [tables (t2/select :model/Table :db_id db-id) | |
models (t2/insert-returning-instances! :model/Card (for [table tables] | |
(merge | |
(t2.with-temp/with-temp-defaults :model/Card) | |
{:table_id (:id table) | |
:type "model" | |
:name (format "Model with table %s" (:name table)) | |
:dataset_query {:database db-id | |
:type :query | |
:query {:source-table (:id table)}}})))] | |
(t2/insert-returning-instances! :model/Card (for [model models] | |
(merge | |
(t2.with-temp/with-temp-defaults :model/Card) | |
{:table_id (:table_id model) | |
:type "question" | |
:name (format "Question with source is card %d" (:id model)) | |
:dataset_query {:database db-id | |
:type :query | |
:aggregation [[:count]] | |
:query {:source-table (format "card__%d" (:id model))}}}))))) | |
(defn create-dashboard-cards | |
[dashboard-id cards] | |
(t2/insert-returning-instances! :model/DashboardCard (for [card cards] | |
(merge | |
(t2.with-temp/with-temp-defaults :model/DashboardCard) | |
{:dashboard_id dashboard-id | |
:card_id (:id card)})))) | |
(defn create-filters! | |
[dashcards n-text-filter n-number-fitler] | |
(let [text-params (for [i (range n-text-filter)] | |
{:name (format "text filter %d" i) | |
:slug (format "text_filter_%d" i) | |
:id (subs (str (random-uuid)) 0 7) | |
:type "string/=" | |
:sectionId "string"}) | |
number-params (for [i (range n-number-fitler)] | |
{:name (format "number filter %d" i) | |
:slug (format "number_filter_%d" i) | |
:id (subs (str (random-uuid)) 0 7) | |
:type "number/=" | |
:sectionId "number"})] | |
(t2/update! :model/Dashboard (:dashboard_id (first dashcards)) | |
{:parameters (concat text-params number-params)}) | |
(doseq [dashcard dashcards] | |
(let [card (t2/select-one :model/Card (:card_id dashcard)) | |
fields (t2/select :model/Field :table_id (:table_id card)) | |
text-fields (filter #(= (:base_type %) :type/Text) fields) | |
number-fields (filter #(= (:base_type %) :type/Integer) fields)] | |
(t2/update! :model/DashboardCard | |
(:id dashcard) | |
{:parameter_mappings (concat | |
(for [[i text-param] (map-indexed vector text-params)] | |
{:parameter_id (:id text-param) | |
:card_id (:card_id dashcard) | |
:target [:dimension [:field (:name (nth text-fields i)) {:base-type :type/Text}]]}) | |
(for [[i number-param] (map-indexed vector number-params)] | |
{:parameter_id (:id number-param) | |
:card_id (:card_id dashcard) | |
:target [:dimension [:field (:name (nth number-fields i)) {:base-type :type/Integer}]]}))}))))) | |
(defn make-big-dashboard | |
[db-id] | |
(let [dashboard-id (t2/insert-returning-pk! :model/Dashboard (t2.with-temp/with-temp-defaults :model/Dashboard)) | |
cards (create-cards db-id) | |
dashcards (create-dashboard-cards dashboard-id cards)] | |
(create-filters! dashcards 10 9) | |
dashboard-id)) | |
(def db (create-db! "big_tables")) | |
(def dashboard-id (make-big-dashboard (:id db))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment