From 032c08abce701cdba04160a6f21a3ee5a8cd3c40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kaan=20Uzdo=C4=9Fan?= Date: Mon, 13 Apr 2026 14:34:21 +0300 Subject: [PATCH] Show parquet export size and DB size on landing page with tooltips Fetches v2/stats.json from export.sourcify.dev and renders two headline figures (parquet export size, Postgres DB size) in the contract stats section. Each figure has an info tooltip breaking down per-table sizes; the parquet tooltip also explains the daily export + zstd compression and links to the docs. Requires verifier-alliance/parquet-export# to be merged first so that v2/stats.json is published. Co-Authored-By: Claude Sonnet 4.6 --- src/pages/LandingPage/VerifiedContracts.tsx | 149 ++++++++++++++++++++ 1 file changed, 149 insertions(+) diff --git a/src/pages/LandingPage/VerifiedContracts.tsx b/src/pages/LandingPage/VerifiedContracts.tsx index 11ec10c..d9d89f5 100644 --- a/src/pages/LandingPage/VerifiedContracts.tsx +++ b/src/pages/LandingPage/VerifiedContracts.tsx @@ -6,6 +6,8 @@ import ChainSelect from "../../components/ChainSelect"; import { motion, useInView } from "framer-motion"; import Button from "../../components/Button"; import { FaDownload } from "react-icons/fa"; +import { FiInfo } from "react-icons/fi"; +import { Tooltip as ReactTooltip } from "react-tooltip"; const NUMBER_OF_TOP_CHAINS = 10; @@ -16,6 +18,36 @@ type statsType = { }; }; +type TableParquetStats = { bytes: number; fileCount: number }; +type TableDbStats = { bytes: number }; +type DatasetStatsType = { + generatedAt: string; + schemaVersion: string; + parquet: { + totalBytes: number; + fileCount: number; + tables: Record; + }; + database: { + totalBytes: number; + tables: Record; + }; +}; + +function formatBytes(bytes: number): string { + if (bytes >= 1e12) return (bytes / 1e12).toFixed(1) + " TB"; + if (bytes >= 1e9) return (bytes / 1e9).toFixed(1) + " GB"; + if (bytes >= 1e6) return (bytes / 1e6).toFixed(1) + " MB"; + return (bytes / 1e3).toFixed(1) + " KB"; +} + +function timeAgo(isoDate: string): string { + const diff = Math.floor((Date.now() - new Date(isoDate).getTime()) / 1000); + if (diff < 3600) return `${Math.floor(diff / 60)}m ago`; + if (diff < 86400) return `${Math.floor(diff / 3600)}h ago`; + return `${Math.floor(diff / 86400)}d ago`; +} + const Chart = () => { const { sourcifyChainMap, sourcifyChains } = useContext(Context); const [selectedChain, setSelectedChain] = useState("1"); @@ -24,6 +56,8 @@ const Chart = () => { const isSectionInView = useInView(sectionRef, { once: true }); const [stats, setStats] = useState(undefined); + const [datasetStats, setDatasetStats] = useState(null); + // Window width to make the chart responsive. We'll change the font sizes and tick width based on this const [windowWidth, setWindowWidth] = useState(window.innerWidth); @@ -45,6 +79,13 @@ const Chart = () => { .catch(() => console.error("error fetching stats")); }, []); + useEffect(() => { + fetch("https://export.sourcify.dev/v2/stats.json") + .then((res) => res.json()) + .then((json) => setDatasetStats(json)) + .catch(() => console.error("error fetching dataset stats")); + }, []); + useEffect(() => { if (stats && isSectionInView) { const total = Object.values(stats || {}).reduce((prev, curr, i) => { @@ -131,6 +172,114 @@ const Chart = () => { 📊 stats.sourcify.dev + + {datasetStats && ( + + {/* Parquet export size */} +
+
+ + {formatBytes(datasetStats.parquet.totalBytes)} + + +
+ parquet export size + updated {timeAgo(datasetStats.generatedAt)} +
+ + {/* Database size */} +
+
+ + {formatBytes(datasetStats.database.totalBytes)} + + +
+ database size +
+ + ( +
+

+ We export our complete Postgres database daily into Apache Parquet format. + Sizes shown are with parquet (zstd) compression. +

+ + Learn more → + + + + + + + + + + + {Object.entries(datasetStats.parquet.tables).map(([table, data]) => ( + + + + + + ))} + +
TableSizeFiles
{table}{formatBytes(data.bytes)}{data.fileCount}
+
+ )} + /> + + ( +
+

+ Live sizes of the Sourcify Postgres database on Google Cloud. Includes indexes and TOAST. + The total also includes system catalogs, so it is slightly larger than the sum of the tables below. +

+ + + + + + + + + {Object.entries(datasetStats.database.tables).map(([table, data]) => ( + + + + + ))} + +
TableSize
{table}{formatBytes(data.bytes)}
+
+ )} + /> +
+ )}