From fa4c7d63b65850b1fcfb9fd23ab15086e55b3084 Mon Sep 17 00:00:00 2001
From: Maximilian Kaske <56969857+mxkaske@users.noreply.github.com>
Date: Mon, 13 Nov 2023 17:48:47 +0100
Subject: [PATCH] fix: timezones (#469)
* chore: timezone playground
* fix: truncate
* fix: missing timezone props
* wip: small adjustements
* fix: og image monitor id
* chore: activate cache
* chore: update tinybird schema and docs
---
apps/docs/developer-guide/setup-env.mdx | 66 ++++++++----
apps/web/src/app/api/og/route.tsx | 8 +-
.../play/_components/timezone-combobox.tsx | 101 ++++++++++++++++++
apps/web/src/app/play/layout.tsx | 4 +-
apps/web/src/app/play/page.tsx | 62 +++++++++--
apps/web/src/components/marketing/example.tsx | 8 +-
.../src/components/status-page/monitor.tsx | 12 ++-
apps/web/src/components/tracker.tsx | 14 ++-
apps/web/src/lib/tb.ts | 6 +-
apps/web/src/lib/tracker.ts | 72 ++++++++-----
.../aggregated_monitor_day_mv.pipe | 4 +-
.../aggregated_monitor_per_day_mv.datasource | 2 +
packages/tinybird/pipes/home_stats.pipe | 2 +-
packages/tinybird/pipes/monitor_list.pipe | 14 ---
packages/tinybird/pipes/status_timezone.pipe | 47 ++++++++
packages/tinybird/src/client.ts | 12 ++-
packages/tinybird/src/validation.ts | 11 +-
17 files changed, 356 insertions(+), 89 deletions(-)
create mode 100644 apps/web/src/app/play/_components/timezone-combobox.tsx
rename packages/tinybird/{pipes => _legacy}/aggregated_monitor_day_mv.pipe (92%)
rename packages/tinybird/{datasources => _legacy}/aggregated_monitor_per_day_mv.datasource (94%)
delete mode 100644 packages/tinybird/pipes/monitor_list.pipe
create mode 100644 packages/tinybird/pipes/status_timezone.pipe
diff --git a/apps/docs/developer-guide/setup-env.mdx b/apps/docs/developer-guide/setup-env.mdx
index 1c8551ffd..ada77c158 100644
--- a/apps/docs/developer-guide/setup-env.mdx
+++ b/apps/docs/developer-guide/setup-env.mdx
@@ -164,7 +164,7 @@ the pipes to the Tinybird server.
### Datasource
-We ingest the monitor response into the `ping_response__v3` table. If you want
+We ingest the monitor response into the `ping_response__v4` table. If you want
to have some dummy data to start with, check out the
[csv](/dummy/ping_response__v3.csv) file.
@@ -181,7 +181,8 @@ data.
We currently have three pipes:
-1. `monitor_list__v0`: returns aggregated data from the `ping_response` table.
+1. `status_timezone__v0`: returns aggregated data from the `ping_response`
+ table.
2. `response_list__v0`: returns the list of responses from the `ping_response`
table.
3. `home_stats__v0`: returns the total amount of pings during a timeframe from
@@ -293,8 +294,8 @@ pip install tinybird-cli
> Please make sure while importing you edit the table header fields in
> camelcase .
-6. Create pipe from the datasource name as a `response_list__v0` and
- `monitor_list__v0`
+6. Create pipe from the datasource name as a `response_list__v1` and
+ `status_timezone__v0`
7. Inside `response_list__v0` pipe, create a endpoint by running a following
query :
@@ -302,7 +303,7 @@ pip install tinybird-cli
```
%
SELECT id, latency, monitorId, pageId, region, statusCode, timestamp, url, workspaceId, cronTimestamp
- FROM ping_response__v3
+ FROM ping_response__v4
WHERE monitorId = {{ String(monitorId, 'openstatusPing') }}
{% if defined(region) %}
AND region = {{ String(region) }}
@@ -320,23 +321,52 @@ pip install tinybird-cli
LIMIT {{Int32(limit, 1000)}}
```
-8. Inside `monitor_list__v0` pipe , create a endpoint by running a following
+8. Inside `status_timezone__v0` pipe , create a endpoint by running a following
query :
```
-%
+VERSION 0
+
+NODE group_by_cronTimestamp
+SQL >
+
+ %
SELECT
- {% if defined(groupBy) and groupBy == "day" %}
- toUnixTimestamp64Milli(toDateTime64(DATE(cronTimestamp / 1000), 3))
- {% else %}
- cronTimestamp
- {% end %}
- AS cronTimestamp, ROUND(AVG(latency)) as avgLatency, COUNT(*) as count, COUNT(CASE WHEN statusCode BETWEEN 200 AND 299 THEN 1 ELSE NULL END) as ok
- FROM ping_response__v3
- WHERE monitorId = {{ String(monitorId, 'openstatusPing') }} AND cronTimestamp IS NOT NULL AND cronTimestamp != 0
- GROUP BY cronTimestamp
- ORDER BY cronTimestamp DESC
- LIMIT {{Int32(limit, 1000)}}
+ toDateTime(cronTimestamp / 1000, 'UTC') AS day,
+ -- only for debugging purposes
+ toTimezone(day, {{ String(timezone, 'Europe/Berlin') }}) as with_timezone,
+ toStartOfDay(with_timezone) as start_of_day,
+ avg(latency) AS avgLatency,
+ count() AS count,
+ count(multiIf((statusCode >= 200) AND (statusCode <= 299), 1, NULL)) AS ok
+ FROM ping_response__v4
+ WHERE
+ (day IS NOT NULL)
+ AND (day != 0)
+ AND monitorId = {{ String(monitorId, '1') }}
+ -- By default, we only only query the last 45 days
+ AND cronTimestamp >= toUnixTimestamp64Milli(
+ toDateTime64(toStartOfDay(date_sub(DAY, 45, now())), 3)
+ )
+ GROUP BY cronTimestamp, monitorId
+ ORDER BY day DESC
+
+
+
+NODE group_by_day
+SQL >
+
+ %
+ SELECT
+ start_of_day as day,
+ sum(count) as count,
+ sum(ok) as ok,
+ round(avg(avgLatency)) as avgLatency
+ FROM group_by_cronTimestamp
+ GROUP BY start_of_day
+ ORDER BY start_of_day DESC
+ LIMIT {{ Int32(limit, 100) }}
+
```
diff --git a/apps/web/src/app/api/og/route.tsx b/apps/web/src/app/api/og/route.tsx
index afe014a0a..9aa2790ee 100644
--- a/apps/web/src/app/api/og/route.tsx
+++ b/apps/web/src/app/api/og/route.tsx
@@ -1,3 +1,4 @@
+import { headers } from "next/headers";
import { ImageResponse } from "next/server";
import { DESCRIPTION, TITLE } from "@/app/shared-metadata";
@@ -13,6 +14,7 @@ const size = {
};
const LIMIT = 40;
+const currentTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
const interRegular = fetch(
new URL("../../../public/fonts/Inter-Regular.ttf", import.meta.url),
@@ -41,16 +43,20 @@ export async function GET(req: Request) {
? searchParams.get("monitorId")
: undefined;
+ const headersList = headers();
+ const timezone = headersList.get("x-vercel-ip-timezone") || currentTimezone;
+
// currently, we only show the tracker for a single(!) monitor
const data =
(monitorId &&
(await getMonitorListData({
monitorId,
limit: LIMIT,
+ timezone,
}))) ||
[];
- const { bars, uptime } = cleanData({ data, last: LIMIT });
+ const { bars, uptime } = cleanData(data, LIMIT);
return new ImageResponse(
(
diff --git a/apps/web/src/app/play/_components/timezone-combobox.tsx b/apps/web/src/app/play/_components/timezone-combobox.tsx
new file mode 100644
index 000000000..834f0b87a
--- /dev/null
+++ b/apps/web/src/app/play/_components/timezone-combobox.tsx
@@ -0,0 +1,101 @@
+"use client";
+
+import * as React from "react";
+import { usePathname, useRouter } from "next/navigation";
+import { Check, ChevronsUpDown } from "lucide-react";
+
+import {
+ Button,
+ Command,
+ CommandEmpty,
+ CommandGroup,
+ CommandInput,
+ CommandItem,
+ Popover,
+ PopoverContent,
+ PopoverTrigger,
+} from "@openstatus/ui";
+
+import useUpdateSearchParams from "@/hooks/use-update-search-params";
+import { cn } from "@/lib/utils";
+
+const currentTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
+const supportedTimezones = Intl.supportedValuesOf("timeZone");
+
+export function TimezoneCombobox({ defaultValue }: { defaultValue?: string }) {
+ const [open, setOpen] = React.useState(false);
+ const pathname = usePathname();
+ const router = useRouter();
+ const updateSearchParams = useUpdateSearchParams();
+
+ const value = defaultValue?.toLowerCase();
+
+ const timezones = supportedTimezones.map((timezone) => ({
+ value: timezone.toLowerCase(),
+ label: timezone,
+ }));
+
+ return (
+
+
+
+
+
+
+
+ No timezone found.
+
+ {timezones.map((timezone) => (
+ {
+ setOpen(false);
+
+ // update search params
+ const searchParams = updateSearchParams({
+ timezone:
+ currentValue === value
+ ? null // remove search param and use default timezone
+ : timezones.find(
+ (timezone) => timezone.value === currentValue,
+ )?.label || null,
+ });
+
+ // refresh page with new search params
+ router.replace(`${pathname}?${searchParams}`);
+ router.refresh();
+ }}
+ >
+
+ {timezone.label}
+
+ ))}
+
+
+
+
+ );
+}
diff --git a/apps/web/src/app/play/layout.tsx b/apps/web/src/app/play/layout.tsx
index 40370b430..40ba3a6f4 100644
--- a/apps/web/src/app/play/layout.tsx
+++ b/apps/web/src/app/play/layout.tsx
@@ -14,11 +14,11 @@ export const metadata: Metadata = {
...defaultMetadata,
twitter: {
...twitterMetadata,
- images: [`/api/og?monitorId=openstatus`],
+ images: [`/api/og?monitorId=1`],
},
openGraph: {
...ogMetadata,
- images: [`/api/og?monitorId=openstatus`],
+ images: [`/api/og?monitorId=1`],
},
};
diff --git a/apps/web/src/app/play/page.tsx b/apps/web/src/app/play/page.tsx
index fa8717331..9bc407e26 100644
--- a/apps/web/src/app/play/page.tsx
+++ b/apps/web/src/app/play/page.tsx
@@ -1,14 +1,55 @@
+import { headers } from "next/headers";
+import * as z from "zod";
+
+import { Label } from "@openstatus/ui";
+
import { Tracker } from "@/components/tracker";
import { getHomeMonitorListData } from "@/lib/tb";
+import { TimezoneCombobox } from "./_components/timezone-combobox";
+
+const supportedTimezones = Intl.supportedValuesOf("timeZone");
+const currentTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
+
+/**
+ * allowed URL search params
+ */
+const searchParamsSchema = z.object({
+ timezone: z.string().optional(),
+});
+
+export default async function PlayPage({
+ searchParams,
+}: {
+ searchParams: { [key: string]: string | string[] | undefined };
+}) {
+ const headersList = headers();
+ const timezone = headersList.get("x-vercel-ip-timezone") || currentTimezone;
+
+ const search = searchParamsSchema.safeParse(searchParams);
+
+ function getDefaultValue() {
+ if (
+ search.success &&
+ search.data.timezone &&
+ supportedTimezones.includes(search.data.timezone)
+ ) {
+ return search.data.timezone;
+ }
+ return timezone;
+ }
+
+ const defaultValue = getDefaultValue();
+
+ const data = await getHomeMonitorListData({ timezone: defaultValue });
-export default async function PlayPage() {
- const data = await getHomeMonitorListData();
return (
-
-
Status
-
- Build your own within seconds.
-
+
+
+
Status
+
+ Build your own within seconds.
+
+
{data && (
)}
+
);
}
diff --git a/apps/web/src/components/marketing/example.tsx b/apps/web/src/components/marketing/example.tsx
index 5b0efe861..d1e2ef728 100644
--- a/apps/web/src/components/marketing/example.tsx
+++ b/apps/web/src/components/marketing/example.tsx
@@ -1,4 +1,5 @@
import { Suspense } from "react";
+import { headers } from "next/headers";
import Link from "next/link";
import { Button } from "@openstatus/ui";
@@ -7,6 +8,8 @@ import { Shell } from "@/components/dashboard/shell";
import { Tracker } from "@/components/tracker";
import { getHomeMonitorListData } from "@/lib/tb";
+const currentTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
+
export async function Example() {
return (
@@ -35,7 +38,9 @@ function ExampleTrackerFallback() {
}
async function ExampleTracker() {
- const data = await getHomeMonitorListData();
+ const headersList = headers();
+ const timezone = headersList.get("x-vercel-ip-timezone") || currentTimezone;
+ const data = await getHomeMonitorListData({ timezone });
if (!data) return null;
return (
);
}
diff --git a/apps/web/src/components/status-page/monitor.tsx b/apps/web/src/components/status-page/monitor.tsx
index 21676e7b2..423e80382 100644
--- a/apps/web/src/components/status-page/monitor.tsx
+++ b/apps/web/src/components/status-page/monitor.tsx
@@ -1,3 +1,4 @@
+import { headers } from "next/headers";
import type { z } from "zod";
import type { selectPublicMonitorSchema } from "@openstatus/db/src/schema";
@@ -5,13 +6,22 @@ import type { selectPublicMonitorSchema } from "@openstatus/db/src/schema";
import { getMonitorListData } from "@/lib/tb";
import { Tracker } from "../tracker";
+const currentTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
+
export const Monitor = async ({
monitor,
}: {
monitor: z.infer;
}) => {
- const data = await getMonitorListData({ monitorId: String(monitor.id) });
+ const headersList = headers();
+ const timezone = headersList.get("x-vercel-ip-timezone") || currentTimezone;
+
+ const data = await getMonitorListData({
+ monitorId: String(monitor.id),
+ timezone,
+ });
if (!data) return Something went wrong
;
+
return (
(isMobile ? 35 : 45), [isMobile]);
- const { bars, uptime } = cleanData({ data, last: maxSize });
+ const { bars, uptime } = cleanData(data, maxSize, timezone);
return (
@@ -134,6 +136,11 @@ const Bar = ({
const toDate = date.setDate(date.getDate() + 1);
const dateFormat = "dd/MM/yy";
+ const className = tracker({
+ variant: blacklist ? "blacklist" : getStatus(ratio).variant,
+ });
+ // console.log({ className, blacklist });
+
return (
setOpen(true)} asChild>
diff --git a/apps/web/src/lib/tb.ts b/apps/web/src/lib/tb.ts
index ca886a9b1..011abd641 100644
--- a/apps/web/src/lib/tb.ts
+++ b/apps/web/src/lib/tb.ts
@@ -37,9 +37,11 @@ export async function getMonitorListData(props: MonitorListParams) {
}
// Includes caching of data for 10 minutes
-export async function getHomeMonitorListData() {
+export async function getHomeMonitorListData(
+ props: Pick,
+) {
try {
- const res = await getHomeMonitorList(tb)({ monitorId: "1" });
+ const res = await getHomeMonitorList(tb)({ monitorId: "1", ...props });
return res.data;
} catch (e) {
console.error(e);
diff --git a/apps/web/src/lib/tracker.ts b/apps/web/src/lib/tracker.ts
index 859d67ee1..868fae9c6 100644
--- a/apps/web/src/lib/tracker.ts
+++ b/apps/web/src/lib/tracker.ts
@@ -36,20 +36,33 @@ export const getStatus = (ratio: number): GetStatusReturnType => {
// TODO: move into Class component sharing the same `data`
-export type CleanMonitor = Monitor & {
+// FIXME: name TrackerMonitor
+
+export type CleanMonitor = {
+ count: number;
+ ok: number;
+ avgLatency: number;
+ cronTimestamp: number;
blacklist?: string;
};
-export function cleanData({ data, last }: { data: Monitor[]; last: number }) {
- const today = new Date();
+/**
+ * Clean the data to show only the last X days
+ * @param data array of monitors
+ * @param last number of days to show
+ * @param timeZone timezone of the monitor
+ * @returns
+ */
+export function cleanData(data: Monitor[], last: number, timeZone?: string) {
+ const today = new Date(new Date().toLocaleString("en-US", { timeZone }));
const currentDay = new Date(today);
- currentDay.setUTCDate(today.getDate());
- currentDay.setUTCHours(0, 0, 0, 0);
+ currentDay.setDate(today.getDate());
+ currentDay.setHours(0, 0, 0, 0);
const lastDay = new Date(today);
- lastDay.setUTCDate(today.getDate() - last);
- lastDay.setUTCHours(0, 0, 0, 0);
+ lastDay.setDate(today.getDate() - last);
+ lastDay.setHours(0, 0, 0, 0);
const dateSequence = generateDateSequence(lastDay, currentDay);
@@ -67,24 +80,26 @@ function fillEmptyData(data: Monitor[], dateSequence: Date[]) {
for (const date of dateSequence) {
const timestamp = date.getTime();
const cronTimestamp =
- dataIndex < data.length ? data[dataIndex].cronTimestamp : undefined;
+ dataIndex < data.length ? data[dataIndex].day.getTime() : undefined;
+
if (
cronTimestamp &&
- areDatesEqualByDayMonthYear(new Date(cronTimestamp), date)
+ areDatesEqualByDayMonthYear(date, new Date(cronTimestamp))
) {
- const isBlacklisted = isInBlacklist(cronTimestamp);
+ const blacklist = isInBlacklist(cronTimestamp);
/**
* automatically remove the data from the array to avoid wrong uptime
* that provides time to remove cursed logs from tinybird via mv migration
*/
- if (isBlacklisted) {
+ if (blacklist) {
filledData.push({
- ...emptyData(timestamp),
- blacklist: blacklistDates[cronTimestamp],
+ ...emptyData(cronTimestamp),
+ blacklist,
});
} else {
- filledData.push(data[dataIndex]);
+ const { day, ...props } = data[dataIndex];
+ filledData.push({ ...props, cronTimestamp });
}
dataIndex++;
} else {
@@ -105,17 +120,17 @@ function emptyData(cronTimestamp: number) {
}
/**
- * equal UTC days - fixes issue with daylight saving
+ * equal days - fixes issue with daylight saving
* @param date1
* @param date2
* @returns
*/
function areDatesEqualByDayMonthYear(date1: Date, date2: Date) {
- date1.setUTCDate(date1.getDate());
- date1.setUTCHours(0, 0, 0, 0);
+ date1.setDate(date1.getDate());
+ date1.setHours(0, 0, 0, 0);
- date2.setUTCDate(date2.getDate());
- date2.setUTCHours(0, 0, 0, 0);
+ date2.setDate(date2.getDate());
+ date2.setHours(0, 0, 0, 0);
return date1.toUTCString() === date2.toUTCString();
}
@@ -132,13 +147,13 @@ export function generateDateSequence(startDate: Date, endDate: Date): Date[] {
while (currentDate <= endDate) {
dateSequence.push(new Date(currentDate));
- currentDate.setUTCDate(currentDate.getDate() + 1);
+ currentDate.setDate(currentDate.getDate() + 1);
}
return dateSequence.reverse();
}
-export function getTotalUptime(data: Monitor[]) {
+export function getTotalUptime(data: { ok: number; count: number }[]) {
const reducedData = data.reduce(
(prev, curr) => {
prev.ok += curr.ok;
@@ -153,7 +168,7 @@ export function getTotalUptime(data: Monitor[]) {
return reducedData;
}
-export function getTotalUptimeString(data: Monitor[]) {
+export function getTotalUptimeString(data: { ok: number; count: number }[]) {
const reducedData = getTotalUptime(data);
const uptime = (reducedData.ok / reducedData.count) * 100;
@@ -163,17 +178,20 @@ export function getTotalUptimeString(data: Monitor[]) {
}
export function isInBlacklist(timestamp: number) {
- return Object.keys(blacklistDates).includes(timestamp.toString());
+ const el = Object.keys(blacklistDates).find((date) =>
+ areDatesEqualByDayMonthYear(new Date(date), new Date(timestamp)),
+ );
+ return el ? blacklistDates[el] : undefined;
}
/**
* Blacklist dates where we had issues with data collection
*/
-export const blacklistDates: Record = {
- 1692921600000:
+export const blacklistDates: Record = {
+ "Fri Aug 25 2023":
"OpenStatus faced issues between 24.08. and 27.08., preventing data collection.",
- 1693008000000:
+ "Sat Aug 26 2023":
"OpenStatus faced issues between 24.08. and 27.08., preventing data collection.",
- 1697587200000:
+ "Wed Oct 18 2023":
"OpenStatus migrated from Vercel to Fly to improve the performance of the checker.",
};
diff --git a/packages/tinybird/pipes/aggregated_monitor_day_mv.pipe b/packages/tinybird/_legacy/aggregated_monitor_day_mv.pipe
similarity index 92%
rename from packages/tinybird/pipes/aggregated_monitor_day_mv.pipe
rename to packages/tinybird/_legacy/aggregated_monitor_day_mv.pipe
index 21c0ad324..da595cbff 100644
--- a/packages/tinybird/pipes/aggregated_monitor_day_mv.pipe
+++ b/packages/tinybird/_legacy/aggregated_monitor_day_mv.pipe
@@ -1,3 +1,5 @@
+# REMINDER: legacy
+
VERSION 0
NODE aggregated_monitor_day_0
@@ -9,7 +11,7 @@ SQL >
avgState(latency) AS avgLatency,
countState() AS count,
countState(multiIf((statusCode >= 200) AND (statusCode <= 299), 1, NULL)) AS ok
- FROM ping_response__v3
+ FROM ping_response__v4
WHERE (day IS NOT NULL) AND (day != 0)
GROUP BY
day,
diff --git a/packages/tinybird/datasources/aggregated_monitor_per_day_mv.datasource b/packages/tinybird/_legacy/aggregated_monitor_per_day_mv.datasource
similarity index 94%
rename from packages/tinybird/datasources/aggregated_monitor_per_day_mv.datasource
rename to packages/tinybird/_legacy/aggregated_monitor_per_day_mv.datasource
index 5f018bc5a..f3a05b093 100644
--- a/packages/tinybird/datasources/aggregated_monitor_per_day_mv.datasource
+++ b/packages/tinybird/_legacy/aggregated_monitor_per_day_mv.datasource
@@ -1,3 +1,5 @@
+# REMINDER: legacy
+
# Data Source created from Pipe 'aggregated_monitor_day_mv'
VERSION 0
diff --git a/packages/tinybird/pipes/home_stats.pipe b/packages/tinybird/pipes/home_stats.pipe
index 85c2c0fcc..5955f688a 100644
--- a/packages/tinybird/pipes/home_stats.pipe
+++ b/packages/tinybird/pipes/home_stats.pipe
@@ -5,7 +5,7 @@ SQL >
%
SELECT COUNT(*) as count
- FROM ping_response__v3
+ FROM ping_response__v4
{% if defined(period) %}
{% if String(period) == "1h" %}
WHERE cronTimestamp > toUnixTimestamp(now() - INTERVAL 1 HOUR) * 1000
diff --git a/packages/tinybird/pipes/monitor_list.pipe b/packages/tinybird/pipes/monitor_list.pipe
deleted file mode 100644
index 5cd36e7de..000000000
--- a/packages/tinybird/pipes/monitor_list.pipe
+++ /dev/null
@@ -1,14 +0,0 @@
-VERSION 1
-
-NODE monitor_list_0
-SQL >
-
- %
- SELECT day as cronTimestamp, round(avgMerge(avgLatency)) avgLatency, countMerge(ok) ok, countMerge(count) count
- FROM aggregated_monitor_per_day_mv
- WHERE monitorId = {{ String(monitorId, 'openstatusPing') }}
- GROUP BY day, monitorId
- ORDER BY cronTimestamp DESC
- LIMIT {{Int32(limit, 100)}}
-
-
diff --git a/packages/tinybird/pipes/status_timezone.pipe b/packages/tinybird/pipes/status_timezone.pipe
new file mode 100644
index 000000000..820f06b85
--- /dev/null
+++ b/packages/tinybird/pipes/status_timezone.pipe
@@ -0,0 +1,47 @@
+VERSION 0
+
+DESCRIPTION >
+ TODO: descripe what it is for!
+
+
+NODE group_by_cronTimestamp
+SQL >
+
+ %
+ SELECT
+ toDateTime(cronTimestamp / 1000, 'UTC') AS day,
+ -- only for debugging purposes
+ toTimezone(day, {{ String(timezone, 'Europe/Berlin') }}) as with_timezone,
+ toStartOfDay(with_timezone) as start_of_day,
+ avg(latency) AS avgLatency,
+ count() AS count,
+ count(multiIf((statusCode >= 200) AND (statusCode <= 299), 1, NULL)) AS ok
+ FROM ping_response__v4
+ WHERE
+ (day IS NOT NULL)
+ AND (day != 0)
+ AND monitorId = {{ String(monitorId, '1') }}
+ -- By default, we only only query the last 45 days
+ AND cronTimestamp >= toUnixTimestamp64Milli(
+ toDateTime64(toStartOfDay(date_sub(DAY, 45, now())), 3)
+ )
+ GROUP BY cronTimestamp, monitorId
+ ORDER BY day DESC
+
+
+
+NODE group_by_day
+SQL >
+
+ %
+ SELECT
+ start_of_day as day,
+ sum(count) as count,
+ sum(ok) as ok,
+ round(avg(avgLatency)) as avgLatency
+ FROM group_by_cronTimestamp
+ GROUP BY start_of_day
+ ORDER BY start_of_day DESC
+ LIMIT {{ Int32(limit, 100) }}
+
+
diff --git a/packages/tinybird/src/client.ts b/packages/tinybird/src/client.ts
index b9d3d8aad..2b5109f51 100644
--- a/packages/tinybird/src/client.ts
+++ b/packages/tinybird/src/client.ts
@@ -24,18 +24,20 @@ export function getResponseList(tb: Tinybird) {
parameters: tbParameterResponseList,
data: tbBuildResponseList,
opts: {
- cache: "no-store",
+ // cache: "no-store",
+ revalidate: 30, // 30 seconds cache
},
});
}
export function getMonitorList(tb: Tinybird) {
return tb.buildPipe({
- pipe: "monitor_list__v1",
+ pipe: "status_timezone__v0",
parameters: tbParameterMonitorList,
data: tbBuildMonitorList,
opts: {
- cache: "no-store",
+ // cache: "no-store",
+ revalidate: 30, // 30 seconds cache
},
});
}
@@ -47,7 +49,7 @@ export function getMonitorList(tb: Tinybird) {
*/
export function getHomeMonitorList(tb: Tinybird) {
return tb.buildPipe({
- pipe: "monitor_list__v1",
+ pipe: "status_timezone__v0",
parameters: tbParameterMonitorList,
data: tbBuildMonitorList,
opts: {
@@ -65,7 +67,7 @@ export function getHomeStats(tb: Tinybird) {
parameters: tbParameterHomeStats,
data: tbBuildHomeStats,
opts: {
- revalidate: 86400, // 60 * 60 * 24 = 86400s - 1d
+ revalidate: 86400, // 60 * 60 * 24 = 86400s = 1d
},
});
}
diff --git a/packages/tinybird/src/validation.ts b/packages/tinybird/src/validation.ts
index c59f4eed9..e7d1766ee 100644
--- a/packages/tinybird/src/validation.ts
+++ b/packages/tinybird/src/validation.ts
@@ -68,21 +68,22 @@ export const tbParameterResponseList = z.object({
});
/**
- * Params for pipe monitor_list__v1
+ * Params for pipe status_timezone__v0
*/
export const tbParameterMonitorList = z.object({
monitorId: z.string(),
- limit: z.number().int().default(60).optional(), // 40 days
+ timezone: z.string().optional(),
+ limit: z.number().int().default(46).optional(), // 46 days
});
/**
- * Values from the pipe monitor_list__v1
+ * Values from the pipe status_timezone__v0
*/
export const tbBuildMonitorList = z.object({
count: z.number().int(),
ok: z.number().int(),
- avgLatency: z.number().int(), // in ms
- cronTimestamp: z.number().int(),
+ avgLatency: z.number().int(),
+ day: z.coerce.date(),
});
/**
--
2.54.0