Skip to content

Commit afb9b58

Browse files
authored
fix: refresh Solana data dashboard twice daily and watermark (#1593)
* fix: reduce data refresh interval to twice daily and update related descriptions * feat: add ChartWatermarkFrame component to enhance chart presentation
1 parent e061b8b commit afb9b58

3 files changed

Lines changed: 57 additions & 30 deletions

File tree

apps/web/src/app/[locale]/data/solana-data-dashboard.tsx

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
import { useSearchParams } from "next/navigation";
1818
import {
1919
Fragment,
20+
type ReactNode,
2021
useCallback,
2122
useEffect,
2223
useMemo,
@@ -84,7 +85,7 @@ const defaultRangeDays = 90;
8485
const emptyRows: MetricRow[] = [];
8586
const kpiCount = 4;
8687
const chartHeight = 320;
87-
const dataRefreshIntervalMs = 24 * 60 * 60 * 1000;
88+
const dataRefreshIntervalMs = 12 * 60 * 60 * 1000;
8889
const dataDedupingIntervalMs = 60 * 1000;
8990
const dataAggregatorRepositoryUrl =
9091
"https://github.com/solana-foundation/solana-data-aggregator";
@@ -999,24 +1000,44 @@ function ChartCard({
9991000
</span>
10001001
</div>
10011002

1002-
{series.length > 0 ? (
1003-
<TimeSeriesChart
1004-
height={chartHeight}
1005-
series={series}
1006-
valueLabel={chart.valueLabel}
1007-
/>
1008-
) : (
1009-
<div className="flex h-[320px] items-center justify-center border border-dashed border-nd-border-light text-sm text-nd-mid-em-text font-brand-mono uppercase tracking-normal">
1010-
{t("empty.noDataForSelection")}
1011-
</div>
1012-
)}
1003+
<ChartWatermarkFrame>
1004+
{series.length > 0 ? (
1005+
<TimeSeriesChart
1006+
height={chartHeight}
1007+
series={series}
1008+
valueLabel={chart.valueLabel}
1009+
/>
1010+
) : (
1011+
<div className="flex h-[320px] items-center justify-center border border-dashed border-nd-border-light text-sm text-nd-mid-em-text font-brand-mono uppercase tracking-normal">
1012+
{t("empty.noDataForSelection")}
1013+
</div>
1014+
)}
1015+
</ChartWatermarkFrame>
10131016

10141017
<ChartOrdinal index={index} />
10151018
{isRefreshing ? <ChartRefreshingOverlay /> : null}
10161019
</article>
10171020
);
10181021
}
10191022

1023+
function ChartWatermarkFrame({ children }: { children: ReactNode }) {
1024+
return (
1025+
<div className="relative">
1026+
<div
1027+
aria-hidden="true"
1028+
className="pointer-events-none absolute inset-x-0 bottom-0 z-0 flex h-[320px] items-center justify-center overflow-hidden"
1029+
>
1030+
<img
1031+
alt=""
1032+
className="h-auto w-[45%] min-w-[220px] max-w-[300px] opacity-[0.1] mix-blend-screen brightness-0 invert"
1033+
src="/src/img/branding/solanaLogo.svg"
1034+
/>
1035+
</div>
1036+
<div className="relative z-[1]">{children}</div>
1037+
</div>
1038+
);
1039+
}
1040+
10201041
function ChartRefreshingOverlay() {
10211042
const t = useTranslations("dataDashboard");
10221043

apps/web/src/app/api/databricks/data/route.ts

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,16 @@ export const runtime = "nodejs";
1919
export const dynamic = "force-dynamic";
2020
export const revalidate = 0;
2121

22-
const DATABRICKS_CACHE_REVALIDATE_SECONDS = 24 * 60 * 60;
22+
const DATABRICKS_CACHE_REVALIDATE_SECONDS = 12 * 60 * 60;
2323
const BROWSER_CACHE_SECONDS = 60;
2424
const EDGE_STALE_SECONDS = 24 * 60 * 60;
2525
const DEFAULT_RANGE_DAYS = 90;
2626
const IS_PRODUCTION = isProduction();
2727
const NO_STORE_CACHE_CONTROL = "no-store, max-age=0";
2828
const DAY_IN_MS = 24 * 60 * 60 * 1000;
2929
const DATA_REFRESH_TIME_ZONE = "America/New_York";
30-
// Source jobs run around 9 AM Eastern; roll dashboard caches at 10 AM Eastern.
31-
const DATA_REFRESH_HOUR = 10;
30+
// Source jobs run around 9 AM and 9 PM Eastern; roll dashboard caches an hour later.
31+
const DATA_REFRESH_HOURS = [10, 22] as const;
3232
const DATA_UNAVAILABLE_ERROR =
3333
"Solana data is unavailable right now. Try again in a moment.";
3434

@@ -153,16 +153,19 @@ function getDatabricksCacheKey(config: DatabricksConfig, rangeDays: number) {
153153

154154
function getDatabricksRefreshCacheKey(now = new Date()) {
155155
const parts = getTimeZoneDateTimeParts(now, DATA_REFRESH_TIME_ZONE);
156+
const latestRefreshHour = getLatestScheduledRefreshHour(parts.hour);
157+
const refreshHour =
158+
latestRefreshHour ?? DATA_REFRESH_HOURS[DATA_REFRESH_HOURS.length - 1];
156159
const refreshDate = new Date(
157160
Date.UTC(
158161
parts.year,
159162
parts.month - 1,
160-
parts.day +
161-
(hasPassedScheduledRefresh(parts, DATA_REFRESH_HOUR) ? 0 : -1),
163+
parts.day + (latestRefreshHour === undefined ? -1 : 0),
164+
refreshHour,
162165
),
163166
);
164167

165-
return refreshDate.toISOString().slice(0, 10);
168+
return `${refreshDate.toISOString().slice(0, 10)}-${String(refreshHour).padStart(2, "0")}`;
166169
}
167170

168171
function getSuccessCacheControl(now = new Date()) {
@@ -190,23 +193,26 @@ function getScheduledRefreshDate(
190193
parts: ReturnType<typeof getTimeZoneDateTimeParts>,
191194
now: Date,
192195
) {
193-
const dayOffset = hasPassedScheduledRefresh(parts, DATA_REFRESH_HOUR) ? 1 : 0;
196+
const nextRefreshHour = getNextScheduledRefreshHour(parts.hour);
197+
const refreshHour = nextRefreshHour ?? DATA_REFRESH_HOURS[0];
198+
const dayOffset = nextRefreshHour === undefined ? 1 : 0;
194199

195200
return zonedTimeToUtc(
196201
parts.year,
197202
parts.month,
198203
parts.day + dayOffset,
199-
DATA_REFRESH_HOUR,
204+
refreshHour,
200205
DATA_REFRESH_TIME_ZONE,
201206
now,
202207
);
203208
}
204209

205-
function hasPassedScheduledRefresh(
206-
parts: ReturnType<typeof getTimeZoneDateTimeParts>,
207-
hour: number,
208-
) {
209-
return parts.hour >= hour;
210+
function getLatestScheduledRefreshHour(hour: number) {
211+
return DATA_REFRESH_HOURS.findLast((refreshHour) => hour >= refreshHour);
212+
}
213+
214+
function getNextScheduledRefreshHour(hour: number) {
215+
return DATA_REFRESH_HOURS.find((refreshHour) => hour < refreshHour);
210216
}
211217

212218
function zonedTimeToUtc(

packages/i18n/messages/web/en/common.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22
"dataDashboard": {
33
"metadata": {
44
"title": "Solana Data",
5-
"description": "Explore Solana network, stablecoin, and DeFi metrics refreshed daily."
5+
"description": "Explore Solana network, stablecoin, and DeFi metrics refreshed twice daily."
66
},
77
"header": {
8-
"eyebrow": "Daily network data",
8+
"eyebrow": "Twice-daily network data",
99
"title": "Solana data",
10-
"description": "Network, stablecoin, and DeFi metrics refreshed daily from leading on-chain providers."
10+
"description": "Network, stablecoin, and DeFi metrics refreshed twice daily from leading on-chain providers."
1111
},
1212
"controls": {
1313
"ariaLabel": "Controls",
@@ -104,9 +104,9 @@
104104
"noProviders": "No providers selected",
105105
"providerListSeparator": " / ",
106106
"lastDays": "Last {days, plural, one {# day} other {# days}}",
107-
"refreshCadence": "Refreshes daily after 10 AM ET",
107+
"refreshCadence": "Refreshes twice daily after 10 AM and 10 PM ET",
108108
"lastRefreshed": "Last refreshed",
109-
"lagNotice": "Lagging by 2 days to support all tools' refresh schedules.",
109+
"lagNotice": "Lagging by 1 day to support all tools' refresh schedules.",
110110
"source": "Data aggregator",
111111
"backfillRequests": "Backfill requests",
112112
"backfillCadence": "Backfills are supported monthly on the 1st."

0 commit comments

Comments
 (0)